Não SAVE TRANSACTION
. Eu nunca encontrei um caso para usar isso. Sei que algumas pessoas preferem isso, mas em tudo o que já fiz em qualquer lugar em que trabalhei, a noção de um erro ocorrendo em qualquer um dos níveis aninhados implicava que qualquer trabalho já realizado era inválido. Ao usar, SAVE TRANSACTION
você está apenas voltando ao estado imediatamente antes da chamada deste Procedimento Armazenado, deixando o processo existente como válido.
Se você quiser obter mais detalhes SAVE TRANSACTION
, consulte as informações nesta resposta:
Como reverter quando três procedimentos armazenados são iniciados em um procedimento armazenado
Outro problema SAVE TRANSACTION
é a nuance de seu comportamento, conforme observado na página do MSDN para SAVE TRANSACTION (ênfase adicionada):
Nomes de ponto de salvamento duplicados são permitidos em uma transação, mas uma instrução ROLLBACK TRANSACTION que especifica o nome do ponto de salvamento reverterá a transação apenas de volta para a SAVE TRANSACTION mais recente usando esse nome.
Ou seja, você precisa ter muito cuidado para atribuir a cada ponto de salvamento em cada procedimento armazenado um nome exclusivo em todos os pontos de salvamento em todos os procedimentos armazenados. Os exemplos a seguir ilustram esse ponto.
Este primeiro exemplo mostra o que acontece quando você reutiliza o nome do Save Point; somente o Save Point de nível mais baixo é revertido.
IF (OBJECT_ID(N'tempdb..#SaveTranTestA') IS NOT NULL)
BEGIN
DROP TABLE #SaveTranTestA;
END;
CREATE TABLE #SaveTranTestA (SomeVal INT NOT NULL);
BEGIN TRAN; -- start level 1
SAVE TRANSACTION MySavePoint;
SELECT @@TRANCOUNT AS [TranCount]; -- 1
INSERT INTO #SaveTranTestA (SomeVal) VALUES (100);
BEGIN TRAN; -- start level 2
SAVE TRANSACTION MySavePoint;
SELECT @@TRANCOUNT AS [TranCount]; -- 2
INSERT INTO #SaveTranTestA (SomeVal) VALUES (200);
COMMIT; -- exit level 2
SELECT @@TRANCOUNT AS [TranCount]; -- 1
SELECT * FROM #SaveTranTestA;
-- 100
-- 200
ROLLBACK TRANSACTION MySavePoint; -- error occurred; undo actions up to this point
SELECT @@TRANCOUNT AS [TranCount]; -- 1
SELECT * FROM #SaveTranTestA;
-- 100
COMMIT; -- exit level 1
SELECT @@TRANCOUNT AS [TranCount]; -- 0
SELECT * FROM #SaveTranTestA;
-- 100
Este segundo exemplo mostra o que acontece quando você usa nomes exclusivos de Save Point; o ponto de salvamento do nível desejado é revertido.
IF (OBJECT_ID(N'tempdb..#SaveTranTestB') IS NOT NULL)
BEGIN
DROP TABLE #SaveTranTestB;
END;
CREATE TABLE #SaveTranTestB (SomeVal INT NOT NULL);
BEGIN TRAN; -- start level 1
SAVE TRANSACTION MySavePointUno;
SELECT @@TRANCOUNT AS [TranCount]; -- 1
INSERT INTO #SaveTranTestB (SomeVal) VALUES (100);
BEGIN TRAN; -- start level 2
SAVE TRANSACTION MySavePointDos;
SELECT @@TRANCOUNT AS [TranCount]; -- 2
INSERT INTO #SaveTranTestB (SomeVal) VALUES (200);
COMMIT; -- exit level 2
SELECT @@TRANCOUNT AS [TranCount]; -- 1
SELECT * FROM #SaveTranTestB;
-- 100
-- 200
ROLLBACK TRANSACTION MySavePointUno; --error occurred; undo actions up to this point
SELECT @@TRANCOUNT AS [TranCount]; -- 1
SELECT * FROM #SaveTranTestB;
-- <no rows>
COMMIT; -- exit level 1
SELECT @@TRANCOUNT AS [TranCount]; -- 0
SELECT * FROM #SaveTranTestB;
-- <no rows>