SAVE TRANSACTION vs BEGIN TRANSACTION (SQL Server) как правильно вкладывать транзакции - PullRequest
15 голосов
/ 15 марта 2012

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

Дело в том - это 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.

Ответы [ 3 ]

21 голосов
/ 17 марта 2012

Полагаю, я уже все понял, поэтому отвечу на свой вопрос ...

Я даже опубликовал свои результаты в блоге, если вы хотите получить более подробную информацию на 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
12 голосов
/ 14 октября 2014

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

Это обеспечивает уникальность имени точки сохранения и использует новые функции 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
3 голосов
/ 03 февраля 2017

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

    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  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...