V tomto článku prozkoumáme SQL Server Nested Transactions, transakční blok s jednou nebo několika transakcemi.
Obrázek popisuje jednoduchý model vnořené transakce.
Vnitřní transakce je uložená procedura, která se skládá z transakčních bloků. MSDN doporučuje „udržovat transakce co nejkratší“, což je zcela opačný postup než u prvního přístupu. Dle mého názoru nedoporučuji používat vnořené transakce. Přesto je někdy musíme použít k řešení některých obchodních problémů.
Takže zjistíme:
- Co se stane, když je vnější transakce odvolána nebo potvrzena?
- Co se stane, když je vnitřní transakce vrácena zpět nebo potvrzena?
- Jak zacházet s chybami vnořených transakcí?
Nejprve vytvoříme ukázkovou tabulku a otestujeme možné případy.
USE AdventureWorks -----Create Demo Table---- CREATE TABLE CodingSightDemo (NumberValue VARCHAR(20))
Případ 1:Vnější i vnitřní transakce jsou potvrzeny.
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
V tomto případě jsou všechny záznamy úspěšně vloženy do tabulky. Předpokládali jsme, že každý příkaz INSERT nevrací chybu.
Případ 2:Vnější transakce je odvolána , vnitřní transakce je potvrzena .
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 vidíte, záznamy se do tabulky nevkládají, protože vnitřní transakce je součástí vnější transakce. Z tohoto důvodu se vnitřní transakce vrátí zpět.
Případ 3:Vnější transakce je potvrzena , vnitřní transakce je vrácena zpět .
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
V tomto případě jsme dostali chybu a do tabulky vložili nejnovější výpis. V důsledku toho vyvstávají některé otázky:
- Proč se zobrazila chyba?
- Proč byl do tabulky přidán nejnovější příkaz INSERT?
Příkaz ROLLBACK TRAN zpravidla vrátí zpět všechny otevřené transakce provedené v aktuální relaci. Nemůžeme napsat dotaz, protože vrátí chybu.
BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') ROLLBACK TRAN ROLLBACK TRAN
Prozkoumáme, jak toto pravidlo může ovlivnit náš případ. Příkaz ROLLBACK TRAN vrátí zpět vnitřní a vnější transakce. Z tohoto důvodu se při spuštění příkazu COMMIT TRAN zobrazí chyba, protože neexistují žádné otevřené transakce.
Dále k tomuto dotazu přidáme příkaz pro zpracování chyb a upravíme jej na základě přístupu defenzivního programování (jak uvádí Wikipedie:Defenzivní programování je forma defenzivního návrhu, která má zajistit trvalou funkci části softwaru za nepředvídaných okolností). Když napíšeme dotaz, aniž bychom se starali o zpracování chyb, a dostaneme chybu, můžeme čelit porušení integrity dat.
S dalším skriptem použijeme body uložení. Označují bod v transakci a pokud chcete, můžete vrátit všechny příkazy DML (Data Manipulation Language) do označeného bodu.
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
Tento dotaz zpracuje chybu, když vnitřní transakce dostane chybu. Také vnější transakce jsou úspěšně potvrzeny. V některých případech však dojde k chybě vnitřní transakce, vnější transakce se musí vrátit zpět. V tomto případě použijeme lokální proměnnou, která zachová a předá vnitřní hodnotu chybového stavu dotazu. Navrhneme vnější dotaz s touto hodnotou proměnné a dotaz bude následující.
--<*************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
Závěry
V tomto článku jsme prozkoumali vnořené transakce a analyzovali, jak zacházet s chybami v tomto typu dotazu. Nejdůležitějším pravidlem u tohoto typu transakce je psát obranné dotazy, protože můžeme získat chybu ve vnějších nebo vnitřních transakcích. Z tohoto důvodu musíme navrhnout chování při zpracování chyb v dotazu.
Odkazy
Vnořené transakce
ULOŽIT TRANSAKCI