Neste artigo, exploraremos as transações aninhadas do SQL Server, um bloco de transação com uma ou várias transações.
A imagem descreve um modelo simples da transação aninhada.
A transação interna é um procedimento armazenado que consiste em blocos de transação. O MSDN recomenda “manter as transações o mais curtas possível” que é totalmente oposta à primeira abordagem. Na minha opinião, não recomendo usar transações aninhadas. Ainda assim, às vezes temos que usá-los para resolver alguns problemas de negócios.
Assim, vamos descobrir:
- O que ocorrerá quando uma transação externa for revertida ou confirmada?
- O que ocorrerá quando uma transação interna for revertida ou confirmada?
- Como lidar com erros de transações aninhadas?
Para começar, vamos criar uma tabela de demonstração e testar possíveis casos.
USE AdventureWorks -----Create Demo Table---- CREATE TABLE CodingSightDemo (NumberValue VARCHAR(20))
Caso 1:as transações externas e internas são confirmadas.
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
Neste caso, todos os registros são inseridos com sucesso na tabela. Assumimos que toda instrução INSERT não retorna um erro.
Caso 2:a transação externa é revertida , a transação interna é confirmada .
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
Como você pode ver, os registros não são inseridos na tabela porque a transação interna faz parte da transação externa. Por esse motivo, a transação interna é revertida.
Caso 3:a transação externa é confirmada , a transação interna é revertida .
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
Nesse caso, recebemos um erro e inserimos a última instrução na tabela. Como resultado, surgem algumas dúvidas:
- Por que recebemos um erro?
- Por que a instrução INSERT mais recente foi adicionada à tabela?
Como regra, a instrução ROLLBACK TRAN reverte todas as transações abertas executadas na sessão atual. Não podemos escrever uma consulta porque ela retornará um erro.
BEGIN TRAN INSERT INTO CodingSightDemo VALUES('One') BEGIN TRAN INSERT INTO CodingSightDemo VALUES('Two') ROLLBACK TRAN ROLLBACK TRAN
Examinaremos como essa regra pode impactar nosso caso. A instrução ROLLBACK TRAN reverte transações internas e externas. Por esse motivo, obtemos um erro ao executar a instrução COMMIT TRAN porque não há transações abertas.
Em seguida, adicionaremos uma instrução de tratamento de erros a essa consulta e a modificaremos com base na abordagem de programação defensiva (como afirma a Wikipedia:A programação defensiva é uma forma de design defensivo destinada a garantir a função contínua de um software sob circunstâncias imprevistas). Quando escrevemos uma consulta sem cuidar do tratamento de erros e obtemos um erro, podemos enfrentar a corrupção da integridade dos dados.
Com o próximo script, usaremos pontos de salvamento. Eles marcam um ponto na transação e, se você quiser, pode reverter todas as instruções DML (Data Manipulation Language) para o ponto marcado.
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
Esta consulta tratará o erro quando a transação interna obtiver um erro. Além disso, as transações externas são confirmadas com êxito. No entanto, em alguns casos, se a transação interna receber um erro, a transação externa deverá ser revertida. Nesse caso, usaremos uma variável local que manterá e passará o valor do estado de erro da consulta interna. Vamos projetar a consulta externa com esse valor de variável e a consulta será a seguinte.
--<*************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
Conclusões
Neste artigo, exploramos as transações aninhadas e analisamos como lidar com erros nesse tipo de consulta. A regra mais importante sobre esse tipo de transação é escrever consultas defensivas porque podemos obter um erro em transações externas ou internas. Por esse motivo, precisamos projetar o comportamento de tratamento de erros da consulta.
Referências
Aninhamento de transações
SALVAR TRANSAÇÃO