Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Umgang mit Fehlern in verschachtelten SQL Server-Transaktionen

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