In diesem Artikel untersuchen wir SQL Server Nested Transactions, einen Transaktionsblock mit einer oder mehreren Transaktionen.
Das Bild beschreibt ein einfaches Modell der verschachtelten Transaktion.
Die innere Transaktion ist eine gespeicherte Prozedur, die aus Transaktionsblöcken besteht. MSDN empfiehlt, „Transaktionen so kurz wie möglich zu halten“, was dem ersten Ansatz völlig entgegengesetzt ist. Meiner Meinung nach empfehle ich nicht, verschachtelte Transaktionen zu verwenden. Trotzdem müssen wir sie manchmal verwenden, um geschäftliche Probleme zu lösen.
Also werden wir herausfinden:
- Was passiert, wenn eine äußere Transaktion rückgängig gemacht oder festgeschrieben wird?
- Was passiert, wenn eine innere Transaktion zurückgesetzt oder festgeschrieben wird?
- Wie gehe ich mit Fehlern bei verschachtelten Transaktionen um?
Zunächst erstellen wir eine Demo-Tabelle und testen mögliche Fälle.
USE AdventureWorks -----Create Demo Table---- CREATE TABLE CodingSightDemo (NumberValue VARCHAR(20))
Fall 1:Sowohl äußere als auch innere Transaktionen werden festgeschrieben.
TRUNCATE TABLE CodingSightDemo --<*************OUTHER TRANSACTION START*************> BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') --<INNER TRANSACTION START> BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') COMMIT TRAN --< INNER TRANSACTION END> INSERT INTO CodingSightDemo VALUES('Three') COMMIT TRAN --<************* OUTHER TRANSACTION END*************> SELECT * FROM CodingSightDemo
In diesem Fall werden alle Datensätze erfolgreich in die Tabelle eingefügt. Wir sind davon ausgegangen, dass jede INSERT-Anweisung keinen Fehler zurückgibt.
Fall 2:Äußere Transaktion wird zurückgerollt , die innere Transaktion wird festgeschrieben .
TRUNCATE TABLE CodingSightDemo --<*************OUTHER TRANSACTION START*************> BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') --<INNER TRANSACTION START> BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') COMMIT TRAN --< INNER TRANSACTION END> INSERT INTO CodingSightDemo VALUES('Three') rollback TRAN --<************* OUTHER TRANSACTION END*************> SELECT * FROM CodingSightDemo
Wie Sie sehen, werden die Datensätze nicht in die Tabelle eingefügt, da die innere Transaktion Teil der äußeren Transaktion ist. Aus diesem Grund wird die innere Transaktion zurückgesetzt.
Fall 3:Äußere Transaktion wird festgeschrieben , wird die innere Transaktion zurückgerollt .
TRUNCATE TABLE CodingSightDemo --<*************OUTHER TRANSACTION START*************> BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') --<INNER TRANSACTION START> BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') ROLLBACK TRAN --< INNER TRANSACTION END> INSERT INTO CodingSightDemo VALUES('Three') COMMIT TRAN --<************* OUTHER TRANSACTION END*************> SELECT * FROM CodingSightDemo
In diesem Fall haben wir einen Fehler erhalten und die neueste Anweisung in die Tabelle eingefügt. Daraus ergeben sich einige Fragen:
- Warum haben wir einen Fehler erhalten?
- Warum wurde die letzte INSERT-Anweisung zur Tabelle hinzugefügt?
In der Regel setzt die Anweisung ROLLBACK TRAN alle offenen Transaktionen zurück, die in der aktuellen Sitzung ausgeführt wurden. Wir können keine Abfrage schreiben, da sie einen Fehler zurückgibt.
BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') ROLLBACK TRAN ROLLBACK TRAN
Wir werden untersuchen, wie sich diese Regel auf unseren Fall auswirken kann. Die Anweisung ROLLBACK TRAN setzt innere und äußere Transaktionen zurück. Aus diesem Grund erhalten wir beim Ausführen der COMMIT TRAN-Anweisung einen Fehler, da keine offenen Transaktionen vorhanden sind.
Als Nächstes fügen wir dieser Abfrage eine Fehlerbehandlungsanweisung hinzu und modifizieren sie basierend auf dem Ansatz der defensiven Programmierung (wie Wikipedia feststellt:Defensive Programmierung ist eine Form des defensiven Designs, mit dem die fortgesetzte Funktion einer Software unter unvorhergesehenen Umständen sichergestellt werden soll). Wenn wir eine Abfrage schreiben, ohne uns um die Fehlerbehandlung zu kümmern, und einen Fehler erhalten, können wir mit einer Beschädigung der Datenintegrität konfrontiert werden.
Mit dem nächsten Skript werden wir Speicherpunkte verwenden. Sie markieren einen Punkt in der Transaktion und wenn Sie möchten, können Sie alle DML-Anweisungen (Data Manipulation Language) bis zum markierten Punkt zurücksetzen.
BEGIN TRY BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') --<INNER TRANSACTION START> SAVE TRANSACTION innerTRAN BEGIN TRY BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') COMMIT TRAN END TRY BEGIN CATCH IF XACT_STATE() <> 0 BEGIN ROLLBACK TRANSACTION innerTRAN PRINT 'Roll back occurs for inner tran' END IF XACT_STATE() <> 0 BEGIN COMMIT TRAN PRINT 'Commit occurs for firt open tran' END END CATCH --< INNER TRANSACTION END> INSERT INTO CodingSightDemo VALUES('Three') COMMIT TRAN END TRY BEGIN CATCH BEGIN IF XACT_STATE() <> 0 ROLLBACK TRAN PRINT 'Roll back occurs for outer tran' END END CATCH --<************* OUTHER TRANSACTION END*************> SELECT * FROM CodingSightDemo
Diese Abfrage behandelt den Fehler, wenn die innere Transaktion einen Fehler erhält. Außerdem werden äußere Transaktionen erfolgreich festgeschrieben. In einigen Fällen muss die äußere Transaktion jedoch zurückgesetzt werden, wenn die innere Transaktion einen Fehler erhält. In diesem Fall verwenden wir eine lokale Variable, die den inneren Abfragefehlerzustandswert behält und weitergibt. Wir werden die äußere Abfrage mit diesem Variablenwert entwerfen und die Abfrage wird wie folgt aussehen.
--<*************OUTHER TRANSACTION START*************> DECLARE @innertranerror as int=0 BEGIN TRY BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') --<INNER TRANSACTION START> SAVE TRANSACTION innerTRAN BEGIN TRY BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') COMMIT TRAN END TRY BEGIN CATCH IF XACT_STATE() <> 0 BEGIN SET @innertranerror=1 ROLLBACK TRANSACTION innerTRAN PRINT 'Roll back occurs for inner tran' END IF XACT_STATE() <> 0 BEGIN COMMIT TRAN PRINT 'Commit occurs for firt open tran' END END CATCH --< INNER TRANSACTION END> INSERT INTO CodingSightDemo VALUES('Three') if @innertranerror=0 BEGIN COMMIT TRAN END IF @innertranerror=1 BEGIN ROLLBACK TRAN END END TRY BEGIN CATCH BEGIN IF XACT_STATE() <> 0 ROLLBACK TRAN PRINT 'Roll back occurs for outer tran' END END CATCH --<************* OUTHER TRANSACTION END*************> SELECT * FROM CodingSightDemo
Schlussfolgerungen
In diesem Artikel haben wir verschachtelte Transaktionen untersucht und analysiert, wie Fehler bei dieser Art von Abfrage behandelt werden können. Die wichtigste Regel für diesen Transaktionstyp ist, defensive Abfragen zu schreiben, da wir bei äußeren oder inneren Transaktionen einen Fehler bekommen können. Aus diesem Grund müssen wir das Fehlerbehandlungsverhalten der Abfrage entwerfen.
Referenzen
Transaktionen verschachteln
TRANSAKTION SPEICHERN