{"id":55000450,"date":"2024-12-01T00:00:00","date_gmt":"2025-03-10T20:17:05","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=450"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"ADODB_Transaktionen_im_SQL_Server","status":"publish","type":"post","link":"https:\/\/vbentwickler.de\/ADODB_Transaktionen_im_SQL_Server\/","title":{"rendered":"ADODB: Transaktionen im SQL Server"},"content":{"rendered":"<p><b>Transaktionen im Kontext von Datenbankanwendungen sind mehrere SQL-Anweisungen zum &Auml;ndern, L&ouml;schen oder Hinzuf&uuml;gen von Daten, die entweder alle oder gar nicht ausgef&uuml;hrt werden. Wir markieren den Start einer solchen Transaktion durch eine bestimmte Methode, f&uuml;hren dann eine oder mehrere SQL-Anweisungen aus und entscheiden dann, ob die Transaktion abgebrochen oder abgeschlossen werden soll. Wenn die Transaktion abgebrochen wird, werden alle seit Beginn der Transaktion in ihrem Kontext durchgef&uuml;hrten &Auml;nderungen r&uuml;ckg&auml;ngig gemacht. In diesem Artikel sehen wir uns an, wie wir solche Transaktionen mit der Execute-Methode von ADODB umsetzen k&ouml;nnen.<\/b><\/p>\n<h2>Methoden zum Steuern von Transaktionen<\/h2>\n<p>Zum Durchf&uuml;hren von Transaktionen bietet die <b>Connection<\/b>-Klasse der ADODB-Bibliothek die folgenden Methoden an:<\/p>\n<ul>\n<li><b>BeginTrans<\/b>: Startet eine Transaktion, um mehrere Operationen als Einheit auszuf&uuml;hren.<\/li>\n<li><b>CommitTrans<\/b>: Best&auml;tigt eine laufende Transaktion und speichert die &Auml;nderungen dauerhaft in der Datenbank.<\/li>\n<li><b>RollbackTrans<\/b>: Bricht eine Transaktion ab und stellt die Daten vor der Transaktion wieder her.<\/li>\n<\/ul>\n<h2>Beispiel f&uuml;r eine Transaktion<\/h2>\n<p>In einem einfachen Beispiel haben wir eine Tabelle namens <b>tblKonten<\/b> angelegt, die wir wie in Bild 1 im SQL Server entworfen haben.<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_06\/pic_450_001.png\" alt=\"Die Tabelle tblKonten im SQL Server\" width=\"549,6265\" height=\"252,5312\" \/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 1: Die Tabelle tblKonten im SQL Server<\/span><\/b><\/p>\n<p>Hier finden wir die Felder <b>KontoID <\/b>(Prim&auml;rschl&uuml;sselfeld der Tabelle), <b>Kontostand <\/b>(Datentyp <b>money<\/b>, speichert den aktuellen Kontostand und darf in diesem Fall nicht kleiner als <b>0 <\/b>werden) und <b>Bezeichnung <\/b>(<b>nvarchar(255)<\/b>, speichert eine Bezeichnung f&uuml;r das Konto).<\/p>\n<p>Die Tabelle kannst Du mit der folgenden T-SQL-Anweisung in einer SQL Server-Datenbank anlegen:<\/p>\n<pre>CREATE TABLE dbo.tblKonten (    KontoID INT IDENTITY(1,1) PRIMARY KEY,    Kontostand MONEY NOT NULL DEFAULT 0         CHECK (Kontostand &gt;= 0),    Bezeichnung NVARCHAR(255) NULL);<\/pre>\n<p>Der besseren &Uuml;bersichtlichkeit halber haben wir der Beispieldatenbank eine Tabellenverkn&uuml;pfung auf diese Tabelle hinzuf&uuml;gt. Die Transaktion wollen wir jedoch nicht auf Basis der Tabellenverkn&uuml;pfungen durchf&uuml;hren (was auch m&ouml;glich w&auml;re), sondern auf Basis der SQL Server-Tabellen &uuml;ber eine entsprechende <b>Connection<\/b>.<\/p>\n<p>Dazu starten wir in der Prozedur aus Listing 1 mit dem Deklarieren eines <b>Connection<\/b>-Objekts namens <b>cnn<\/b>. Danach &ouml;ffnen wir die Verbindung mit der <b>Open<\/b>-Methode.<\/p>\n<pre><span style=\"color:blue;\">Public Sub <\/span>TransaktionBank()\r\n     <span style=\"color:blue;\">Dim <\/span>cnn<span style=\"color:blue;\"> As <\/span>ADODB.Connection\r\n     <span style=\"color:blue;\">Set<\/span> cnn = <span style=\"color:blue;\">New<\/span> ADODB.Connection\r\n     cnn.ConnectionString = \"Provider=MSOLEDBSQL;Data Source=amvDesktop2023;\" _\r\n         & \"Initial Catalog=Transaktionen;Integrated Security=SSPI;\"\r\n     cnn.Open\r\n     \r\n     cnn.BeginTrans\r\n     \r\n     <span style=\"color:blue;\">On Error GoTo<\/span> Fehler\r\n      \r\n     ''1 Geld von Konto A abbuchen\r\n     cnn.Execute \"UPDATE tblKonten SET Kontostand = Kontostand - 100 WHERE KontoID = 1\"\r\n     \r\n     ''2 Geld auf Konto B gutschreiben\r\n     cnn.Execute \"UPDATE tblKonten SET Kontostand = Kontostand + 100 WHERE KontoID = 2\"\r\n     <span style=\"color:blue;\">On Error GoTo<\/span> 0\r\n     \r\n     cnn.CommitTrans\r\n     <span style=\"color:blue;\">MsgBox<\/span> \"&Uuml;berweisung erfolgreich!\", vbInformation\r\n     <span style=\"color:blue;\">Exit Sub<\/span>\r\n     \r\nFehler:\r\n     cnn.RollbackTrans\r\n     <span style=\"color:blue;\">MsgBox<\/span> \"Fehler aufgetreten - &Uuml;berweisung wurde storniert!\", <span style=\"color:blue;\">vbCr<\/span>itical\r\n<span style=\"color:blue;\">End Sub<\/span><\/pre>\n<p><b><span style=\"color:darkgrey;\">Listing 1: Diese Prozedur f&uuml;hrt eine einfache Transaktion durch.<\/span><\/b><\/p>\n<p>Anschlie&szlig;end starten wir bereits die Transaktion mit der Methode <b>BeginTrans <\/b>des <b>Connection<\/b>-Objekts. Diese Methode hat keine Parameter. Die einzige Eigenschaft, die sie hat, ist das Objekt, in dessen Kontext sie aufgerufen wird &#8211; sie bezieht sich nun auf die Verbindung aus <b>cnn<\/b>.<\/p>\n<p>Danach wollen wir eine Banktransaktion ausf&uuml;hren, die sich vereinfacht aus dem Abbuchen eines bestimmten Betrags von einem Konto und dem Hinzubuchen des gleichen Betrags zu einem anderen Konto zusammensetzt.<\/p>\n<p>Dazu sind zwei <b>UPDATE<\/b>-Anweisung n&ouml;tig, mit denen wir die betroffenen Datens&auml;tze anpassen.<\/p>\n<p>Dass dabei etwas nicht funktioniert, wollen wir am Auftreten eines Fehlers festmachen.<\/p>\n<p>Dazu deaktivieren wir die eingebaute Fehlerbehandlung und sorgen mit <b>On Error Resume Fehler<\/b> daf&uuml;r, dass der Code bei einem Fehler an der Sprungmarke <b>Fehler <\/b>weiterl&auml;uft.<\/p>\n<p>Anschlie&szlig;end f&uuml;hren wir die erste der beiden <b>UPDATE<\/b>-Anweisungen durch, mit der wir den Kontostand des Kontos mit dem Wert <b>1 <\/b>im Feld <b>KontoID <\/b>um <b>100 <\/b>verringern.<\/p>\n<p>Die zweite <b>UPDATE<\/b>-Anweisung erh&ouml;ht den Wert des zweiten Kontos um 100.<\/p>\n<p>Da hier bereits ein entsprechender Fehler aufgetreten sein sollte, k&ouml;nnen wir die eingebaute Fehlerbehandlung mit <b>On Error Goto 0 <\/b>wieder aktivieren.<\/p>\n<p>Schlie&szlig;lich f&uuml;hren wir mit der <b>CommitTrans<\/b>-Methode des <b>Connection<\/b>-Objekts die Transaktion endg&uuml;ltig durch und liefern eine entsprechende Erfolgsmeldung.<\/p>\n<p>Es kann jedoch auch ein Fehler aufgetreten sein. Zu Beispielzwecken haben wir dazu bei der Tabellendefinition angegeben, dass der Wert des Feldes <b>Kontostand <\/b>niemals kleiner als <b>0 <\/b>werden darf.<\/p>\n<p>Wenn wir also eine &Uuml;berweisung durchf&uuml;hren, die mit der ersten <b>UPDATE<\/b>-Anweisung daf&uuml;r sorgt, dass der Kontostand auf dem ersten Konto kleiner wird als <b>0<\/b>, l&ouml;st dies einen Fehler aus und wir landen bei der Sprungmarke <b>Fehler<\/b>.<\/p>\n<p>Hier wird die Transaktion mit der Methode <b>RollbackTrans <\/b>komplett r&uuml;ckabgewickelt und es erscheint eine entsprechende Meldung (siehe Bild 2).<\/p>\n<p class=\"image\"><img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2024_06\/pic_450_002.png\" alt=\"Die Tabelle tblKonten nach einem Fehler innerhalb der Transaktion\" width=\"549,6265\" height=\"323,5008\"\/><\/p>\n<p><b><span style=\"color:darkgrey;\">Bild 2: Die Tabelle tblKonten nach einem Fehler innerhalb der Transaktion<\/span><\/b><\/p>\n<h2>Das ACID-Prinzip<\/h2>\n<p>Transaktionen sollen das Einhalten des <b>ACID<\/b>-Prinzips sicherstellen. Dieses besteht aus:<\/p>\n<ul>\n<li><b>Atomicity <\/b>(Atomarit&auml;t): Entweder alle &Auml;nderungen oder keine &#8211; keine halben Updates.<\/li>\n<li><b>Consistency <\/b>(Konsistenz): Datenbank bleibt immer in einem g&uuml;ltigen Zustand.<\/li>\n<li><b>Isolation <\/b>(Isolation): Eine Transaktion sieht keine halbfertigen &Auml;nderungen anderer.<\/li>\n<li><b>Durability <\/b>(Dauerhaftigkeit): Nach <b>CommitTrans<\/b> sind &Auml;nderungen dauerhaft gespeichert.<\/li>\n<\/ul>\n<p>Was genau geschieht bei der Transaktion, wenn diese fehlschl&auml;gt?<\/p>\n<ul>\n<li>Die Transaktion wird gestartet.<\/li>\n<li>Die erste <b>UPDATE<\/b>-Anweisung wird ausgef&uuml;hrt und der Betrag wird vom ersten Konto abgezogen.<\/li>\n<li>Die &Auml;nderung wird nur im Speicher (Buffer Pool) und in der Transaction Log-Datei (LDF) gespeichert &#8211; aber noch nicht endg&uuml;ltig in die Datenbank (MDF) geschrieben.<\/li>\n<li>Die zweite <b>UPDATE<\/b>-Anweisung schl&auml;gt fehl (zum Beispiel, weil das Konto nicht existiert).<\/li>\n<li>Die Prozedur erkennt den Fehler und f&uuml;hrt die <b>RollbackTrans<\/b>-Methode aus.<\/li>\n<li>Alle &Auml;nderungen seit <b>BeginTrans <\/b>werden verworfen, und der vorherige Zustand wird aus dem Transaktionsprotokoll (Transaction Log) wiederhergestellt.<\/li>\n<\/ul>\n<h2>Kann ich den aktuellen Zustand w&auml;hrend einer Transaktion pr&uuml;fen?<\/h2>\n<p>Wir k&ouml;nnen den aktuellen Zustand pr&uuml;fen, aber bei den Standardeinstellungen nur innerhalb der Transaktion &#8211; das ist sowohl bez&uuml;glich des Zeitraums als auch bez&uuml;glich der Verbindung zu sehen.<\/p>\n<p>Wir k&ouml;nnen also zum Beispiel zwischen die erste und die zweite <b>UPDATE<\/b>-Anweisung die folgenden Anweisungen einf&uuml;gen, um die zu diesem Zeitpunkt g&uuml;ltigen Kontost&auml;nde zu betrachten (das Beispiel enth&auml;lt nur die beiden Konten, daher m&uuml;ssen wir hier nicht filtern):<\/p>\n<pre><span style=\"color:blue;\">Dim <\/span>rst<span style=\"color:blue;\"> As <\/span>ADODB.Recordset\r\n<span style=\"color:blue;\">Set<\/span> rst = cnn.Execute(\"SELECT * FROM tblKonten\")\r\n<span style=\"color:blue;\">Do While<\/span> <span style=\"color:blue;\">Not<\/span> rst.EOF\r\n     <span style=\"color:blue;\">Debug.Print<\/span> rst!KontoID, rst!Kontostand\r\n     rst.Move<span style=\"color:blue;\">Next<\/span>\r\n<span style=\"color:blue;\">Loop<\/span><\/pre>\n<p>Hier sehen wir den bereits reduzierten Kontostand des ersten Kontos und den noch nicht erh&ouml;hten Kontostand des zweiten Kontos.<\/p>\n<p>Das war der zeitliche Aspekt. Wir k&ouml;nnen den Kontostand nicht von au&szlig;erhalb der Verbindung betrachten.  Wir k&ouml;nnen also nicht zum Beispiel die Tabellenverkn&uuml;pfung auf diese Tabelle in Access &ouml;ffnen. Der Grund ist, dass die Tabelle beziehungsweise die betroffenen Datens&auml;tze zu diesem Zeitpunkt durch die Transaktion gesperrt sind.<\/p>\n<p>Das ist wichtig, damit nicht ein anderer Prozess in der Zwischenzeit auf diese Datens&auml;tze zugreift und sie &auml;ndert.<\/p>\n<p>Wir k&ouml;nnen aber ungeachtet von Einstellungen mit dem folgenden Befehl vom SQL Server Management Studio aus auf die aktuellen Daten der Tabelle zugreifen:<\/p>\n<pre>SELECT * FROM tblKonten WITH (NOLOCK);<\/pre>\n<p>Dies liefert im aktuellen Beispiel den bereits reduzierten Wert im Feld <b>Kontostand <\/b>des ersten Datensatzes und den noch nicht ge&auml;nderten Wert des zweiten Kontos.<\/p>\n<p>Das Verhalten w&auml;hrend der Durchf&uuml;hrung von Transaktionen k&ouml;nnen wir in einem bestimmten Rahmen steuern.<\/p>\n<p>Dazu h&auml;lt die <b>Connection<\/b>-Klasse f&uuml;r uns die Eigenschaft <b>IsolationLevel <\/b>vor.<\/p>\n<h2>IsolationLevel in ADODB: Transaktionssicherheit und Parallelit&auml;t<\/h2>\n<p>Die <b>IsolationLevel<\/b>-Eigenschaft in <b>ADODB.Connection <\/b>bestimmt, wie eine Transaktion mit konkurrierenden Datenbankzugriffen umgeht.<\/p>\n<p>Sie legt fest, welche &Auml;nderungen von anderen Transaktionen sichtbar sind und sch&uuml;tzt so vor Inkonsistenzen wie <b>Dirty Reads<\/b>, <b>Non-Repeatable Reads <\/b>und <b>Phantom Reads<\/b> (wir erl&auml;utern diese Begriffe nachfolgend). Je nach gew&auml;hltem Wert f&uuml;r die Eigenschaft <b>IsolationLevel <\/b>steigt die Datensicherheit, jedoch oft auf Kosten der Parallelit&auml;t.<\/p>\n<ul>\n<li>Bei <b>adXactReadUncommitted <\/b>(<b>IsolationLevel = 1<\/b>) k&ouml;nnen Dirty Reads auftreten, was bedeutet, dass eine Transaktion &Auml;nderungen sehen kann, die von einer anderen Transaktion noch nicht best&auml;tigt wurden. Falls diese zweite Transaktion sp&auml;ter zur&uuml;ckgesetzt wird (<b>RollbackTrans<\/b>), k&ouml;nnten die gelesenen Daten falsch oder inkonsistent sein. Dieses Level ist zwar performant, aber risikobehaftet.<\/li>\n<li>Ein sicherer Standard ist <b>adXactReadCommitted <\/b>(<b>IsolationLevel = 2<\/b>). Hier werden Dirty Reads verhindert, da nur bereits commitete Daten von anderen Transaktionen gelesen werden k&ouml;nnen. Allerdings sind Non-Repeatable Reads m&ouml;glich, bei denen sich Daten w&auml;hrend einer Transaktion &auml;ndern k&ouml;nnen, wenn eine andere Transaktion sie modifiziert und best&auml;tigt.<\/li>\n<li>Mit <b>adXactRepeatableRead <\/b>(<b>IsolationLevel = 3<\/b>) wird zus&auml;tzlich sichergestellt, dass Daten w&auml;hrend einer Transaktion nicht ge&auml;ndert oder gel&ouml;scht werden, was Non-Repeatable Reads verhindert. Allerdings k&ouml;nnen weiterhin Phantom Reads auftreten &#8211; das bedeutet, dass sich die Ergebnismenge einer Abfrage ver&auml;ndert, wenn eine andere Transaktion neue Zeilen hinzuf&uuml;gt.<\/li>\n<li>Das h&ouml;chste Sicherheitsniveau bietet <b>adXactSerializable <\/b>(<b>IsolationLevel = 4<\/b>). Hier werden Dirty Reads, Non-Repeatable Reads und Phantom Reads vollst&auml;ndig ausgeschlossen, indem andere Transaktionen w&auml;hrend der Laufzeit gesperrt werden. Dies sorgt f&uuml;r maximale Datenintegrit&auml;t, kann aber zu langen Sperrzeiten und reduzierter Parallelit&auml;t f&uuml;hren.<\/li>\n<li>Eine moderne Alternative stellt <b>adXactSnapshot <\/b>(<b>IsolationLevel = 4096<\/b>) dar, die in SQL Server &uuml;ber Row Versioning arbeitet. Anstatt Sperren zu setzen, erh&auml;lt jede Transaktion eine eigene konsistente Sicht auf die Daten, ohne andere Transaktionen zu blockieren. Das verhindert Sperrkonflikte und bietet eine gute Balance zwischen Konsistenz und Performance.<\/li>\n<\/ul>\n<p>Die Wahl des richtigen Wertes f&uuml;r die Eigenschaft <b>IsolationLevel <\/b>h&auml;ngt von den Anforderungen der Anwendung ab: H&ouml;here Isolation bietet mehr Datensicherheit, kann aber die Performance beeintr&auml;chtigen. In Szenarien mit vielen parallelen Zugriffen ist daher ein durchdachtes Transaktionsmanagement entscheidend.<\/p>\n<p>Die Eigenschaft <b>IsolationLevel <\/b>stellen wir f&uuml;r das <b>Connection<\/b>-Objekt ein. <\/p>\n<h2>Deadlocks durch Transaktionen<\/h2>\n<p>Ein Deadlock tritt in SQL Server auf, wenn zwei oder mehr Transaktionen sich gegenseitig blockieren und keiner der Prozesse fortfahren kann, weil jeder auf eine Ressource wartet, die von einem anderen Prozess gesperrt wurde.<\/p>\n<p>Dadurch entsteht eine zyklische Blockade, die nur durch einen Eingriff von SQL Server oder den Abbruch einer der Transaktionen aufgel&ouml;st werden kann. <\/p>\n<h2>Beispiel f&uuml;r einen Deadlock<\/h2>\n<p>Wenn wir die oben angegebene Operation, bei der wir zuerst den Datensatz mit der ID 1 &auml;ndern und dann den mit derr ID 2, in einer parallel stattfindenden Transaktion andersherum ausf&uuml;hren, kann es zu einem Deadlock kommen.<\/p>\n<p>Dazu m&uuml;ssten wir zwei Access-Instanzen &ouml;ffnen, von denen eine die Beispielprozedur <b>Deadlock1 <\/b>schrittweise ausf&uuml;hrt und eine die Beispielprozedur <b>Deadlock2<\/b>. Wir &ouml;ffnen also zwei Mal Access und darin jeweils die gleiche Datenbank.<\/p>\n<p>Deadlock1 enth&auml;lt innerhalb der Transaktion die folgenden beiden Anweisungen:<\/p>\n<pre>cnn.Execute \"UPDATE tblKonten SET Guthaben = Guthaben - 100 WHERE KontoID = 1\"\r\ncnn.Execute \"UPDATE tblKonten SET Guthaben = Guthaben + 100 WHERE KontoID = 2\"<\/pre>\n<p>In der Prozedur <b>Deadlock2<\/b>, die wir in der zweiten Session ausf&uuml;hren, finden wir die Anweisungen in dieser Reihenfolge:<\/p>\n<pre>cnn.Execute \"UPDATE tblKonten SET Guthaben = Guthaben + 100 WHERE KontoID = 2\"\r\ncnn.Execute \"UPDATE tblKonten SET Guthaben = Guthaben - 100 WHERE KontoID = 1\"<\/pre>\n<p>Nun f&uuml;hren wir schrittweise die Prozedur <b>Deadlock1 <\/b>in der ersten Session aus bis zur Anweisung, die Konto 1 &auml;ndert.<\/p>\n<p>Dann f&uuml;hren wir in der zweiten Session schrittweise die Prozedur <b>Deadlock2 <\/b>aus und bleiben bei der Anweisung h&auml;ngen, die Konto 1 &auml;ndern will.<\/p>\n<p>Wir k&ouml;nnen nun noch in <b>Deadlock1 <\/b>in der ersten Session fortfahren, bleiben dort aber auch h&auml;ngen.<\/p>\n<p>Der Deadlock entsteht hier, weil die beiden Sessions sich gegenseitig blockieren.<\/p>\n<p>Schlie&szlig;lich bricht Session 2 mit einem Fehler aus der Blockade aus, weil ein Timeout auftritt.<\/p>\n<p>Dies geschieht normalerweise nach 30 Sekunden. Wir k&ouml;nnen diesen Zeitraum aber durch Einstellen der Eigenschaft <b>ConnectionTimeout <\/b>des <b>Connection<\/b>-Objekts auf einen anderen Wert wie beispielsweise <b>10 <\/b>&auml;ndern.<\/p>\n<p>Einen solchen Deadlock k&ouml;nnen wir &uuml;brigens verhindern, indem wir ausschlie&szlig;en, dass &Auml;nderungsanweisungen wie hier in unterschiedlicher Reihenfolge auf die beiden gleichen Datens&auml;tze auswirken.<\/p>\n<h2>Vermeintlicher Deadlock bei identischer Reihenfolge<\/h2>\n<p>Wenn wir in beiden Sessions die Prozedur <b>Deadlock1 <\/b>aufrufen und schrittweise durchlaufen und bei der ersten Session nach der Aktualisierung des Datensatzes mit dem Prim&auml;rschl&uuml;sselwert <b>1 <\/b>stehenbleiben und das gleiche in der zweiten Session erledigen, bleibt die Verarbeitung ebenfalls stehen.<\/p>\n<p>Dabei handelt es sich nun aber nicht um einen Deadlock, sondern um eine Datensatzsperre, die verhindert, dass auch die zweite Session auf den Datensatz mit dem Prim&auml;rschl&uuml;sselwert <b>1 <\/b>zugreift.<\/p>\n<p>Wo ist der Unterschied? Es gibt keine Blockade. Die zweite Session wird nur solange blockiert, bis die erste Session den Datensatz mit dem Prim&auml;rschl&uuml;sselwert 1 nicht mehr sperrt, was in der Regel schnell geschieht.<\/p>\n<h2>Zusammenfassung und Ausblick<\/h2>\n<p>Das Konzept der Transaktionen ist wichtig f&uuml;r Vorg&auml;nge, die nur durchgef&uuml;hrt werden sollen, wenn jeder einzelne Teilschritt abgeschlossen ist.<\/p>\n<p>Deshalb bietet ADODB mit den drei Anweisungen <b>BeginTrans<\/b>, <b>CommitTrans <\/b>und <b>RollbackTrans <\/b>die M&ouml;glichkeit, mehrere Anweisungen zu einer Transaktion zusammenzufassen, wobei wir vor dem Aufruf von <b>CommitTrans <\/b>oder <b>RollbackTrans <\/b>pr&uuml;fen k&ouml;nnen, ob alle Anweisungen erfolgreich waren.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Transaktionen im Kontext von Datenbankanwendungen sind mehrere SQL-Anweisungen zum &Auml;ndern, L&ouml;schen oder Hinzuf&uuml;gen von Daten, die entweder alle oder gar nicht ausgef&uuml;hrt werden. Wir markieren den Start einer solchen Transaktion durch eine bestimmte Methode, f&uuml;hren dann eine oder mehrere SQL-Anweisungen aus und entscheiden dann, ob die Transaktion abgebrochen oder abgeschlossen werden soll. Wenn die Transaktion abgebrochen wird, werden alle seit Beginn der Transaktion in ihrem Kontext durchgef&uuml;hrten &Auml;nderungen r&uuml;ckg&auml;ngig gemacht. In diesem Artikel sehen wir uns an, wie wir solche Transaktionen mit der Execute-Methode von ADODB umsetzen k&ouml;nnen.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[662024,66062024,44000008,44000020],"tags":[],"yst_prominent_words":[],"class_list":["post-55000450","post","type-post","status-publish","format-standard","hentry","category-662024","category-66062024","category-Datenzugriffstechnik","category-Entity_Framework_Core"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000450","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/comments?post=55000450"}],"version-history":[{"count":0,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/posts\/55000450\/revisions"}],"wp:attachment":[{"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/media?parent=55000450"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/categories?post=55000450"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/tags?post=55000450"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/vbentwickler.de\/data\/wp\/v2\/yst_prominent_words?post=55000450"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}