Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Jak radzić sobie z błędami w zagnieżdżonych transakcjach SQL Server

W tym artykule omówimy SQL Server Nested Transactions, blok transakcji z jedną lub kilkoma transakcjami.

Obraz przedstawia prosty model zagnieżdżonej transakcji.

Transakcja wewnętrzna to procedura składowana, która składa się z bloków transakcji. MSDN zaleca „utrzymywanie jak najkrótszych transakcji”, co jest całkowitym przeciwieństwem pierwszego podejścia. Moim zdaniem nie polecam korzystania z transakcji zagnieżdżonych. Mimo to czasami musimy ich użyć do rozwiązania niektórych problemów biznesowych.

W ten sposób dowiemy się:

  • Co się stanie, gdy zewnętrzna transakcja zostanie wycofana lub zatwierdzona?
  • Co się stanie, gdy wewnętrzna transakcja zostanie wycofana lub zatwierdzona?
  • Jak radzić sobie z błędami transakcji zagnieżdżonych?

Na początek utworzymy tabelę demonstracyjną i przetestujemy możliwe przypadki.

USE AdventureWorks
-----Create Demo Table----
CREATE TABLE CodingSightDemo
(NumberValue VARCHAR(20))

Przypadek 1:Zarówno transakcje zewnętrzne, jak i wewnętrzne są zatwierdzone.

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

W takim przypadku wszystkie rekordy są pomyślnie wstawiane do tabeli. Założyliśmy, że każda instrukcja INSERT nie zwraca błędu.

Przypadek 2:Transakcja zewnętrzna jest wycofana , transakcja wewnętrzna jest zatwierdzona .

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

Jak widać, rekordy nie są wstawiane do tabeli, ponieważ transakcja wewnętrzna jest częścią transakcji zewnętrznej. Z tego powodu wewnętrzna transakcja cofa się.

Przypadek 3:transakcja zewnętrzna jest zatwierdzona , transakcja wewnętrzna jest cofana .

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

W tym przypadku otrzymaliśmy błąd i wstawiliśmy do tabeli najnowszą instrukcję. W rezultacie pojawiają się pytania:

  • Dlaczego otrzymaliśmy błąd?
  • Dlaczego do tabeli dodano najnowszą instrukcję INSERT?

Z reguły instrukcja ROLLBACK TRAN wycofuje wszystkie otwarte transakcje wykonane w bieżącej sesji. Nie możemy napisać zapytania, ponieważ zwróci ono błąd.

BEGIN TRAN
INSERT INTO CodingSightDemo	
VALUES('One')	
BEGIN TRAN
INSERT INTO CodingSightDemo	
VALUES('Two')	
ROLLBACK TRAN
ROLLBACK TRAN

Zbadamy, jak ta zasada może wpłynąć na naszą sprawę. Instrukcja ROLLBACK TRAN wycofuje transakcje wewnętrzne i zewnętrzne. Z tego powodu otrzymujemy błąd podczas uruchamiania instrukcji COMMIT TRAN, ponieważ nie ma otwartych transakcji.

Następnie dodamy do tego zapytania instrukcję obsługi błędów i zmodyfikujemy ją w oparciu o podejście programowania defensywnego (jak podaje Wikipedia:Programowanie defensywne to forma projektowania defensywnego, której celem jest zapewnienie ciągłości działania oprogramowania w nieprzewidzianych okolicznościach). Kiedy piszemy zapytanie, nie dbając o obsługę błędów i otrzymujemy błąd, możemy spotkać się z uszkodzeniem integralności danych.

W kolejnym skrypcie użyjemy punktów zapisu. Zaznaczają punkt w transakcji, a jeśli chcesz, możesz wycofać wszystkie instrukcje DML (język manipulacji danymi) do zaznaczonego punktu.

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

To zapytanie obsłuży błąd, gdy wewnętrzna transakcja otrzyma błąd. Również transakcje zewnętrzne są pomyślnie zatwierdzane. Jednak w niektórych przypadkach, jeśli transakcja wewnętrzna otrzyma błąd, transakcja zewnętrzna musi zostać wycofana. W tym przypadku użyjemy zmiennej lokalnej, która zachowa i przekaże wewnętrzną wartość stanu błędu zapytania. Zaprojektujemy zewnętrzne zapytanie z tą wartością zmiennej, a zapytanie będzie wyglądać następująco.

--<*************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

Wnioski

W tym artykule zbadaliśmy zagnieżdżone transakcje i przeanalizowaliśmy, jak radzić sobie z błędami w tego typu zapytaniach. Najważniejszą zasadą dotyczącą tego typu transakcji jest pisanie zapytań obronnych, ponieważ możemy uzyskać błąd w transakcjach zewnętrznych lub wewnętrznych. Z tego powodu musimy zaprojektować zachowanie obsługi błędów zapytania.

Referencje

Zagnieżdżanie transakcji

ZAPISZ TRANSAKCJĘ


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL Server 2016:zapisywanie wyników zapytania w pliku CSV

  2. Utwórz tabelę HTML za pomocą SQL FOR XML

  3. Konwertuj kolumnę Datetime z UTC na czas lokalny w instrukcji select

  4. Jak znaleźć wszystkie połączone podgrafy grafu nieskierowanego?

  5. Jak NTILE() działa w SQL Server