Sql server 在存储过程中使用SAVE TRANSACTION SavePointName

Sql server 在存储过程中使用SAVE TRANSACTION SavePointName,sql-server,tsql,stored-procedures,transactions,savepoints,Sql Server,Tsql,Stored Procedures,Transactions,Savepoints,我不清楚是否需要为我使用的每个SP使用不同的保存点名称保存事务 即使较高级别的事务使用相同的保存点名称,我是否可以始终使用例如保存事务过程保存点和回滚事务过程保存点 我的SP签名如下: ALTER PROCEDURE [dbo].[usp_MyTask]() AS BEGIN DECLARE @iReturn int = 0 DECLARE @tranCount int = @@TRANCOUNT; IF @tranCount > 0 SAVE T

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

即使较高级别的事务使用相同的保存点名称,我是否可以始终使用例如
保存事务过程保存点
回滚事务过程保存点

我的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
ALTER过程[dbo]。[usp\u MyTask]()
作为
开始
声明@iReturn int=0
声明@tranCount int=@@tranCount;
如果@tranCount>0
保存事务保存点;
其他的
开始训练
...
如果
开始
@i返回=1
去撤销
结束
...
如果@tranCount=0
提交传输
返回
撤消:
如果@tranCount=0——事务已在过程中启动。回滚完整事务。
回滚传输;
其他的
如果XACT_STATE()-1回滚事务保存点;
返回@iReturn
结束

希望我的问题是清楚的。

从技术上讲,是的,您可以重复使用相同的存储点名称,并且它们会堆积起来,就像对
BEGIN-TRAN
的多次调用一样,其中对
COMMIT
的每次调用都会使计数器递减。也就是说,如果您发出
savetransactionprocsavepoint
5次,然后调用
rollbacktransactionprocsavepoint
2次,在第三次调用
SAVE TRAN
和第四次调用它之前,您仍将保持事物所处的状态

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

  • 由于刚才提到的行为,在嵌套场景中,根据调用
    转到Undo
    的条件,如果您有一种情况,即调用嵌套过程5个级别,然后级别5成功完成,然后级别4成功完成,但级别3决定转到“Undo”,它将执行
    回滚事务ProcSavePoint只会回滚第五级。这会让你处于一种糟糕的状态,因为你的目的是要回到3级开始时的状态

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

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

  • 我不相信
    XACT\u STATE()
    可以在
    TRY
    /
    CATCH
    构造之外报告
    -1


  • 首先为什么要使用保存点?您是否遇到过这样的情况:即使子过程调用中发生错误,外层也可能继续并最终提交
    COMMIT

    我最常使用的模板显示在我对DBA.StackExchange上此问题的回答中:。该模板只是在开始时检查活动事务(类似于您的方法),但如果存在活动事务,则不会执行任何操作。因此,永远不会调用额外的
    BEGIN-TRAN
    或甚至
    SAVE-TRAN
    ,只有后面的外部(即使是app-code)将执行
    COMMIT
    ROLLBACK

    请指出这一点,因为您的代码与我在链接答案中发布的代码之间似乎存在功能上的差异,但事实并非如此:没有特别需要捕获
    @@TRANCOUNT
    的实际值,因为唯一的选项是
    0
    >0
    ,除非进入模板时
    @@TRANCOUNT
    已经大于1,否则它将得到的最大值无论如何都是1(如果触发器和/或
    插入到…EXEC
    增量中,即使有活动事务,也可能是2)。在这两种情况下,我对
    @InNestedTransaction
    使用
    变量在功能上/逻辑上等同于将
    @@tracount
    存储在
    INT
    变量中,因为
    SAVE TRAN
    不会递增
    @@tracount


  • 您是否在询问是否可以在另一个会话中使用相同的保存点名称?是否存在外层可能会继续并最终提交的情况,即使子进程调用中发生错误?对调用者进程从子进程返回一个返回值,并在需要时回滚。我假设对于未处理的sql server异常,整个转换都会回滚,不是吗?在任何情况下,我总是向我的客户机应用程序报告返回代码。请让我再评估一下你的答案…@zig听起来你确实有一个很好的理由去做TRY/CATCH和你的“undo”标签,我猜这将出现在
    TRY
    块的末尾。如果没有TRY/CATCH构造,管理起来就有点困难,因为有些错误只中止语句,有些则中止批处理。如果使用
    将XACT\u ABORT设置为ON
    ,则所有错误将中止整个批处理,并在
    CATCH
    块内从
    XACT\u STATE()
    返回
    -1
    ;但是,我不喜欢在
    上使用
    XACT\u ABORT。谢谢@srutzky,你给了我很多好信息。我可能需要重新考虑我的模板,因为我总是返回一个表示托管错误的值(通常是验证错误)。永远不要使用TRY/CATCH思维
    XACT\u ABORT ON
    始终处于启用状态:/@zig我对您上面的代码有一个问题。此语句是否“如果'@tranCount'=0提交传输返回”正确。我的意思是,如果Trancount为零,那么COMMIT TRAN将给出错误。如果我在这里错了或不见了,请纠正我something@sam,
    @tranCount=0
    (局部变量最初设置为
    。@@TRAN