3

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

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

我的 SP(s) 签名如下:

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

希望我的问题很清楚。

4

1 回答 1

4

从技术上讲,是的,您可以重复使用相同的保存点名称,它们会像多次调用一样堆叠起来BEGIN TRAN,每次调用都会COMMIT简单地递减计数器。意思是,如果你发出SAVE TRANSACTION ProcSavePoint;5 次,然后调用ROLLBACK TRANSACTION ProcSavePoint;2 次,你仍然会处于SAVE TRAN第三次调用之后和第四次调用之前的状态。

但是,此代码在几个层面上存在问题:

  1. 由于刚才提到的行为,在嵌套场景中,根据调用的条件GOTO Undo,如果您有调用嵌套 procs 5 层深度,然后第 5 层成功完成,然后第 4 层成功完成的情况,但随后第 3 级决定去“撤消”,它将执行ROLLBACK TRANSACTION ProcSavePoint;只会回滚第 5 级。这会使您处于糟糕的状态,因为其目的是回滚到第 3 级开始时的状态。

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

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

  3. 我不相信XACT_STATE()可以报告/构造-1之外的内容。TRYCATCH

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

    我最常使用的模板显示在我在 DBA.StackExchange 上对这个问题的回答中:我们需要在 C# 代码和存储过程中处理事务吗。该模板只是在开始时检查活动事务(类似于您的方法),但如果有活动事务则什么也不做。所以永远不会有额外的BEGIN TRAN甚至SAVE TRAN被调用,只有后来的外部(即使是应用程序代码),才会执行COMMITor ROLLBACK

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

于 2016-02-28T16:29:18.743 回答