I denne artikel vil vi udforske SQL Server Nested Transactions, en transaktionsblok med en eller flere transaktioner.
Billedet beskriver en simpel model af den indlejrede transaktion.
Den indre transaktion er en lagret procedure, der består af transaktionsblokke. MSDN anbefaler "at holde transaktioner så korte som muligt", hvilket er totalt modsat den første tilgang. Efter min mening anbefaler jeg ikke at bruge indlejrede transaktioner. Alligevel er vi nogle gange nødt til at bruge dem til at løse nogle forretningsproblemer.
Derfor skal vi finde ud af:
- Hvad sker der, når en ydre transaktion rulles tilbage eller forpligtes?
- Hvad sker der, når en indre transaktion rulles tilbage eller forpligtes?
- Hvordan håndterer man indlejrede transaktionsfejl?
Til at begynde med vil vi oprette en demo-tabel og teste mulige cases.
USE AdventureWorks -----Create Demo Table---- CREATE TABLE CodingSightDemo (NumberValue VARCHAR(20))
Case 1:Både ydre og indre transaktioner er forpligtet.
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
I dette tilfælde er alle posterne indsat i tabellen. Vi antog, at hver INSERT-sætning ikke returnerer en fejl.
Tilfælde 2:Ydre transaktion er rullet tilbage , indre transaktion er forpligtet .
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
Som du kan se, er posterne ikke indsat i tabellen, fordi den indre transaktion er en del af den ydre transaktion. Af denne grund ruller den indre transaktion tilbage.
Tilfælde 3:Ydre transaktion er forpligtet , indre transaktion rulles tilbage .
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
I dette tilfælde fik vi en fejl og indsatte den seneste erklæring i tabellen. Som følge heraf opstår nogle spørgsmål:
- Hvorfor fik vi en fejl?
- Hvorfor blev den seneste INSERT-sætning føjet til tabellen?
Som regel ruller ROLLBACK TRAN-sætningen alle åbne transaktioner tilbage, der er udført i den aktuelle session. Vi kan ikke skrive en forespørgsel, fordi den vil returnere en fejl.
BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') ROLLBACK TRAN ROLLBACK TRAN
Vi vil undersøge, hvordan denne regel kan påvirke vores sag. ROLLBACK TRAN-sætningen ruller indre og ydre transaktioner tilbage. Af denne grund får vi en fejl, når vi kører COMMIT TRAN-sætningen, fordi der ikke er nogen åbne transaktioner.
Dernæst vil vi tilføje en fejlhåndteringserklæring til denne forespørgsel og ændre den baseret på den defensive programmeringstilgang (som Wikipedia siger:Defensiv programmering er en form for defensivt design beregnet til at sikre den fortsatte funktion af et stykke software under uforudsete omstændigheder). Når vi skriver en forespørgsel uden at tage os af fejlhåndtering og får en fejl, kan vi blive udsat for korruption af dataintegriteten.
Med det næste script vil vi bruge sparepoint. De markerer et punkt i transaktionen, og hvis du vil, kan du rulle alle DML-udsagn (Data Manipulation Language) tilbage til det markerede punkt.
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
Denne forespørgsel vil håndtere fejlen, når den indre transaktion får en fejl. Også ydre transaktioner er begået med succes. Men i nogle tilfælde, hvis den indre transaktion får en fejl, skal den ydre transaktion rulle tilbage. I dette tilfælde vil vi bruge en lokal variabel, der vil beholde og videregive den indre forespørgselsfejltilstandsværdi. Vi designer den ydre forespørgsel med denne variabelværdi, og forespørgslen vil være som følger.
--<*************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
Konklusioner
I denne artikel undersøgte vi indlejrede transaktioner og analyserede, hvordan man håndterer fejl i denne type forespørgsel. Den vigtigste regel om denne transaktionstype er at skrive defensive forespørgsler, fordi vi kan få en fejl i ydre eller indre transaktioner. Af denne grund er vi nødt til at designe fejlhåndteringsadfærd for forespørgslen.
Referencer
Indlejringstransaktioner
GEM TRANSAKTION