我不清楚我是否需要为每个使用SAVE TRANSACTION
的SP使用不同的保存点名称。
我可以一直使用,例如SAVE TRANSACTION ProcedureSavePoint
和ROLLBACK 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
希望我的问题很明确。
答案 0 :(得分:4)
从技术上讲,是的,您可以重复使用相同的保存点名称,它们会像多次调用BEGIN TRAN
一样堆叠起来,每次调用COMMIT
只会减少计数器的数量。这意味着,如果您发出SAVE TRANSACTION ProcSavePoint;
5次,然后再拨打ROLLBACK TRANSACTION ProcSavePoint;
2次,那么在第三次调用SAVE TRAN
之后,在调用它之前,您仍将处于状态第四次。
但是,此代码在几个级别上存在问题:
由于刚才提到的行为,在嵌套场景中,根据调用GOTO Undo
的条件,如果你有一种情况,你可以调用嵌套的procs 5级深度,然后是level 5成功完成,然后4级成功完成,但是3级决定进入&#34;撤消&#34;,它将执行ROLLBACK TRANSACTION ProcSavePoint;
,它将仅回滚第5级。这会让你处于糟糕状态,因为目的是回滚到3级开始时所处的状态。
使用唯一的保存点名称可以纠正此问题。
您奇怪地没有使用TRY
/ CATCH
构造。你真的应该。如果您有逻辑决定根据不是SQL Server错误的特定条件取消操作,您仍然可以通过调用RAISERROR()
立即转到CATCH
块来强制执行此操作。或者,如果您不想将其视为错误,除了GOTO undo
/ TRY
之外,您仍然可以执行CATCH
方法。
我不相信XACT_STATE()
可以在-1
/ TRY
构造之外报告CATCH
。
为什么您首先使用保存点?即使子进程调用中发生错误,您是否有外层可能会继续并最终COMMIT
的情况?
我最常使用的模板显示在我对DBA.StackExchange的问题的回答中:Are we required to handle Transaction in C# Code as well as in Store procedure。该模板只是在开始时检查活动事务(类似于您的方法),但如果存在活动事务则不执行任何操作。因此,永远不会有其他BEGIN TRAN
甚至SAVE TRAN
被调用,只有外部更晚(即使它是应用代码),才会执行COMMIT
或ROLLBACK
。< / p>
并且只是指出了这一点,因为它看起来就像你的代码和我在该链接答案中发布的内容之间的功能差异,但实际上并非如此:没有特定的需要捕获@@TRANCOUNT
的实际值,因为唯一的选项是0
或> 0
,除非@@TRANCOUNT
已经是&gt; 1在输入模板时,无论如何它将获得的最大值为1(如果触发器和/或INSERT INTO ... EXEC
增加,即使存在活动事务,也可能为2)。在任何一种情况下,我对BIT
使用@InNestedTransaction
变量在功能上/逻辑上等同于将@@TRANCOUNT
存储在INT
变量中,因为SAVE TRAN
不会增加@@TRANCOUNT
{1}}。