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Ę