2016-02-28 55 views
3

目前还不清楚我是否需​​要为每个使用SAVE TRANSACTION的SP使用不同的保存点名称。在存储过程中使用SAVE TRANSACTION SavePointName

我能否总是使用例如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 

希望我的问题是清楚的。

+0

你问是否相同保存点名称可以在另一个会话 –

回答

4

从技术上来说,是的,您可以重复使用相同的保存点名称,它们将叠加起来,就像对BEGIN TRAN的多次调用一样,每次调用COMMIT时只需递减计数器。意思是,如果您发出5次SAVE TRANSACTION ProcSavePoint;,然后再拨打ROLLBACK TRANSACTION ProcSavePoint;两次,您将仍然处于第三次呼叫SAVE TRAN之后并在第四次呼叫之前所处的状态。

然而,这段代码是有问题的几个层次:

  1. 由于行为刚才提到,在嵌套方案中,根据不同的条件(S)调用GOTO Undo,如果你有一个情况你在这里调用嵌套特效5级,然后5级成功完成,然后4级成功完成,但3级决定去“撤消”,它将执行ROLLBACK TRANSACTION ProcSavePoint;,这将只回滚第五级。这会让你处于一个糟糕的状态,因为其意图是回滚到第3级开始时的状态。

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

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

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

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

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

    而只是有这个指出,因为它看起来你的代码,什么之间的功能差异我张贴在该链接的答案,但真的不是:有没有具体的需要陷阱@@TRANCOUNT实际值因为唯一的选项是0> 0,并且除非@@TRANCOUNT在输入模板时已经大于1,否则无论如何将获得的最大值为1(如果触发器和/或INSERT INTO ... EXEC增加(即使存在活动事务)也可能为1。在任何一种情况下,我使用@InNestedTransactionBIT变量在功能上/逻辑上相当于将@@TRANCOUNT存储在INT变量中,因为SAVE TRAN不会增加@@TRANCOUNT

+0

使用你有在外层可能会继续的情况下,最终即使有错误在proc子调用发生COMMIT?是。调用者proc从子进程返回一个返回值,并在需要时返回。我假设对于未处理的SQL Server异常,整个翻译会回滚,不是吗?在任何情况下,我总是使用返回码向我的客户端应用程序报告。让我评估你的答案请多一点... – zig

+0

@zig这听起来像你这样做,然后有一个很好的理由做TRY/CATCH _and_你的“撤消”标签,我想这将在' TRY'块。如果没有TRY/CATCH构造,事情会有点难以管理,因为一些错误会中止声明并且会中止批处理。如果使用SET XACT_ABORT ON,那么_all_错误会中止整个批处理,并从'CATCH'块内的'XACT_STATE()'返回'-1';不过,我不喜欢使用'XACT_ABORT ON'。 –

+0

谢谢@srutzky,你给了我很多很好的信息。我可能需要重新考虑我的模板,因为我总是返回一个表示托管错误的值(通常是验证错误)。并且从不使用TRY/CATCH思考'XACT_ABORT ON'始终开启:/ – zig