SAVE TRANSACTION vs BEGIN TRANSACTION(SQL Server)如何很好地嵌套事务

时间:2012-03-15 02:51:55

标签: sql-server transactions nested-transactions

我有一个存储过程需要设置一个保存点,以便在某些情况下可以撤消它所做的一切并将错误代码返回给调用者,或接受/提交它并将成功返回给调用者。但无论调用者是否已经开始交易,我都需要它。该文件在这个问题上非常混乱。这是我认为可行的,但我不确定所有后果。

事情是 - 其他人调用此Stored Procedure (SP)。所以我不知道他们是否已经开始交易......即使我要求用户开始使用我的SP交易,我仍然对Save Points的正确使用有疑问...... / p>

我的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 个答案:

答案 0 :(得分:21)

我相信我现在已经全力以赴了,所以我会回答我自己的问题......

如果您想了解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

答案 1 :(得分:12)

扩展Brian B's answer

这可确保保存点名称是唯一的,并使用SQL Server 2012的新TRY / CATCH / THROW功能。

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

答案 2 :(得分:3)

我在我的存储过程中使用过这种类型的事务管理器:

    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