SAVE TRANSACTION vs BEGIN TRANSACTION (SQL Server) how to nest transactions nicely

前端 未结 3 1519
生来不讨喜
生来不讨喜 2020-12-24 14:10

I have a stored procedure that needs to set a save point so that it can, under certain circumstances, undo everything it did and return an error code to the caller, or accep

相关标签:
3条回答
  • 2020-12-24 15:00

    I have used this type of transaction manager in my Stored Procedures :

        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  
    
    0 讨论(0)
  • 2020-12-24 15:03

    Extending Brian B's answer.

    This ensures the save point name is unique and uses the new TRY/CATCH/THROW features of 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
    
    0 讨论(0)
  • 2020-12-24 15:13

    I believe I've figured this all out now, so I will answer my own question...

    I've even blogged my findings if you want more details at http://geekswithblogs.net/bbiales/archive/2012/03/15/how-to-nest-transactions-nicely---quotbegin-transactionquot-vs-quotsave.aspx

    So my SP starts with something like this, to start a new transaction if there is none, but use a Save Point if one is already in progress:

    DECLARE @startingTranCount int
    SET @startingTranCount = @@TRANCOUNT
    
    IF @startingTranCount > 0
        SAVE TRANSACTION mySavePointName
    ELSE
        BEGIN TRANSACTION
    -- …
    

    Then, when ready to commit the changes, you only need to commit if we started the transaction ourselves:

    IF @startingTranCount = 0
        COMMIT TRANSACTION
    

    And finally, to roll back just your changes so far:

    -- Roll back changes...
    IF @startingTranCount > 0
        ROLLBACK TRANSACTION MySavePointName
    ELSE
        ROLLBACK TRANSACTION
    
    0 讨论(0)
提交回复
热议问题