SAVE TRANSACTION vs BEGIN TRANSACTION (SQL Server), как правильно вкладывать транзакции

StackOverflow https://stackoverflow.com//questions/9713350

Вопрос

У меня есть хранимая процедура, которой необходимо установить точку сохранения, чтобы она могла при определенных обстоятельствах отменить все, что она сделала, и вернуть код ошибки вызывающей стороне или принять/зафиксировать ее и вернуть успех вызывающей стороне.Но мне нужно, чтобы он работал независимо от того, начал ли уже вызывающий абонент транзакцию или нет.Документ чрезвычайно запутан по этому вопросу.Вот что, я думаю, сработает, но я не уверен во всех последствиях.

Дело в том - это Stored Procedure (SP) зовут другие.Так что я не знаю, начали они транзакцию или нет...Даже если я требую, чтобы пользователи начали транзакцию для использования моего SP, у меня все равно остаются вопросы о правильном использовании Save Points ...

Мой SP проверит, выполняется ли транзакция, а если нет, запустит ее с помощью BEGIN TRANSACTION.Если транзакция уже выполняется, вместо этого будет создана точка сохранения с SAVE TRANSACTION MySavePointName, и за исключением того факта, что это то, что я сделал.

Тогда, если мне придется откатить мои изменения, если я сделал BEGIN TRANSACTION раньше, тогда я ROLLBACK TRANSACTION.Если я сделал точку сохранения, то я ROLLBACK TRANSACTION MySavePointName.Кажется, этот сценарий отлично работает.

Вот здесь я немного запутался - если я хочу сохранить проделанную работу, если я начал транзакцию, я ее выполню. COMMIT TRANSACTION.А если бы я создал точку сохранения?Я пытался COMMIT TRANSACTION MySavePointName, но затем вызывающая сторона пытается зафиксировать транзакцию и получает ошибку:

Запрос COMMIT TRANSACTION не имеет соответствующей BEGIN TRANSACTION.

Вот мне и интересно - точку сохранения можно откатить (это работает: ROLLBACK TRANSACTION MySavePointName НЕ откатывает транзакцию вызывающего абонента).Но, возможно, никогда не нужно «совершать» это?Он просто остается там на случай, если вам понадобится вернуться к нему, но исчезает после фиксации (или отката) исходной транзакции?

Если есть «лучший» способ «вложить» транзакцию, пожалуйста, пролейте немного света.Я не придумал, как совместить BEGIN TRANSACTION но только откатить или зафиксировать мою внутреннюю транзакцию.Кажется ROLLBACK всегда будет откатываться к верхней транзакции, в то время как COMMIT просто уменьшает @@trancount.

Это было полезно?

Решение

Думаю, теперь я все это понял, поэтому отвечу на свой вопрос...

Я даже опубликовал свои выводы в блоге, если вам нужна более подробная информация: http://geekswithblogs.net/bbiales/archive/2012/03/15/how-to-nest-transactions-nicely---quotbegin-transactionquot-vs-quotsave.aspx

Итак, мой SP начинается примерно с этого: начать новую транзакцию, если ее нет, но использовать точку сохранения, если она уже выполняется:

DECLARE @startingTranCount int
SET @startingTranCount = @@TRANCOUNT

IF @startingTranCount > 0
    SAVE TRANSACTION mySavePointName
ELSE
    BEGIN TRANSACTION
-- …

Затем, когда вы будете готовы зафиксировать изменения, вам нужно будет зафиксировать их только в том случае, если мы сами начали транзакцию:

IF @startingTranCount = 0
    COMMIT TRANSACTION

И, наконец, чтобы откатить только ваши изменения:

-- Roll back changes...
IF @startingTranCount > 0
    ROLLBACK TRANSACTION MySavePointName
ELSE
    ROLLBACK TRANSACTION

Другие советы

Расширение Ответ Брайана Б..

Это гарантирует уникальность имени точки сохранения и использование новых функций TRY/CATCH/THROW SQL Server 2012.

DECLARE @mark CHAR(32) = replace(newid(), '-', '');
DECLARE @trans INT = @@TRANCOUNT;

IF @trans = 0
    BEGIN TRANSACTION @mark;
ELSE
    SAVE TRANSACTION @mark;

BEGIN TRY
    -- do work here

    IF @trans = 0
        COMMIT TRANSACTION @mark;
END TRY
BEGIN CATCH
    IF xact_state() = 1 OR (@trans = 0 AND xact_state() <> 0) ROLLBACK TRANSACTION @mark;
    THROW;
END CATCH

Я использовал этот тип менеджера транзакций в своих хранимых процедурах:

    CREATE PROCEDURE Ardi_Sample_Test  
        @InputCandidateID INT  
    AS  
        DECLARE @TranCounter INT;  
        SET @TranCounter = @@TRANCOUNT;  
        IF @TranCounter > 0  
            SAVE TRANSACTION ProcedureSave;  
        ELSE  
            BEGIN TRANSACTION;  
        BEGIN TRY  

            /*
            <Your Code>
            */

            IF @TranCounter = 0  
                COMMIT TRANSACTION;  
        END TRY  
        BEGIN CATCH  
            IF @TranCounter = 0  
                ROLLBACK TRANSACTION;  
            ELSE  
                IF XACT_STATE() <> -1  
                    ROLLBACK TRANSACTION ProcedureSave;  

            DECLARE @ErrorMessage NVARCHAR(4000);  
            DECLARE @ErrorSeverity INT;  
            DECLARE @ErrorState INT;  
            SELECT @ErrorMessage = ERROR_MESSAGE();  
            SELECT @ErrorSeverity = ERROR_SEVERITY();  
            SELECT @ErrorState = ERROR_STATE();  

            RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);  
        END CATCH  
    GO  
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top