在存储过程中使用SAVE TRANSACTION SavePointName

时间:2016-02-28 14:41:51

标签: sql-server tsql stored-procedures transactions savepoints

我不清楚我是否需​​要为每个使用SAVE TRANSACTION的SP使用不同的保存点名称。

我可以一直使用,例如SAVE TRANSACTION ProcedureSavePointROLLBACK TRANSACTION ProcedureSavePoint,即使更高级别的交易使用相同的保存点名称?

我的SP签名如下:

ALTER PROCEDURE [dbo].[usp_MyTask]()
AS
BEGIN
    DECLARE @iReturn int = 0

    DECLARE @tranCount int = @@TRANCOUNT;
    IF @tranCount > 0
        SAVE TRANSACTION ProcSavePoint;
    ELSE
        BEGIN TRAN    
    ...

    IF <some condition>
    BEGIN
        @iReturn = 1
        GOTO Undo
    END

    ...

    IF @tranCount = 0 
        COMMIT TRAN 
    RETURN

Undo:
    IF @tranCount = 0 -- transaction started in procedure. Roll back complete transaction.
        ROLLBACK TRAN;
    ELSE
        IF XACT_STATE() <> -1 ROLLBACK TRANSACTION ProcSavePoint;

    RETURN @iReturn
END

希望我的问题很明确。

1 个答案:

答案 0 :(得分:4)

从技术上讲,是的,您可以重复使用相同的保存点名称,它们会像多次调用BEGIN TRAN一样堆叠起来,每次调用COMMIT只会减少计数器的数量。这意味着,如果您发出SAVE TRANSACTION ProcSavePoint; 5次,然后再拨打ROLLBACK TRANSACTION ProcSavePoint; 2次,那么在第三次调用SAVE TRAN之后,在调用它之前,您仍将处于状态第四次。

但是,此代码在几个级别上存在问题:

  1. 由于刚才提到的行为,在嵌套场景中,根据调用GOTO Undo的条件,如果你有一种情况,你可以调用嵌套的procs 5级深度,然后是level 5成功完成,然后4级成功完成,但是3级决定进入&#34;撤消&#34;,它将执行ROLLBACK TRANSACTION ProcSavePoint;,它将仅回滚第5级。这会让你处于糟糕状态,因为目的是回滚到3级开始时所处的状态。

    使用唯一的保存点名称可以纠正此问题。

  2. 您奇怪地没有使用TRY / CATCH构造。你真的应该。如果您有逻辑决定根据不是SQL Server错误的特定条件取消操作,您仍然可以通过调用RAISERROR()立即转到CATCH块来强制执行此操作。或者,如果您不想将其视为错误,除了GOTO undo / TRY之外,您仍然可以执行CATCH方法。

  3. 我不相信XACT_STATE()可以在-1 / TRY构造之外报告CATCH

  4. 为什么您首先使用保存点?即使子进程调用中发生错误,您是否有外层可能会继续并最终COMMIT的情况?

    我最常使用的模板显示在我对DBA.StackExchange的问题的回答中:Are we required to handle Transaction in C# Code as well as in Store procedure。该模板只是在开始时检查活动事务(类似于您的方法),但如果存在活动事务则不执行任何操作。因此,永远不会有其他BEGIN TRAN甚至SAVE TRAN被调用,只有外部更晚(即使它是应用代码),才会执行COMMITROLLBACK。< / p>

    并且只是指出了这一点,因为它看起来就像你的代码和我在该链接答案中发布的内容之间的功能差异,但实际上并非如此:没有特定的需要捕获@@TRANCOUNT的实际值,因为唯一的选项是0> 0,除非@@TRANCOUNT已经是&gt; 1在输入模板时,无论如何它将获得的最大值为1(如果触发器和/或INSERT INTO ... EXEC增加,即使存在活动事务,也可能为2)。在任何一种情况下,我对BIT使用@InNestedTransaction变量在功能上/逻辑上等同于将@@TRANCOUNT存储在INT变量中,因为SAVE TRAN不会增加@@TRANCOUNT {1}}。