在存储过程中使用 SAVE TRANSACTION SavePointName
Using SAVE TRANSACTION SavePointName in a Stored Procedure
我不清楚我是否需要为我使用的每个 SP 使用不同的保存点名称 SAVE TRANSACTION
。
我可以一直使用例如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
希望我的问题很清楚。
从技术上讲,是的,您可以重复使用相同的保存点名称,它们将像多次调用 BEGIN TRAN
一样堆叠起来,每次调用 COMMIT
只会使计数器递减。意思是,如果你发出 SAVE TRANSACTION ProcSavePoint;
5 次,然后调用 ROLLBACK TRANSACTION ProcSavePoint;
2 次,你仍然会停留在第三次调用 SAVE TRAN
之后和调用它之前的状态第四次。
但是,此代码在几个层面上存在问题:
由于刚才提到的行为,在嵌套场景中,根据调用 GOTO Undo
的条件,如果您遇到调用嵌套过程 5 层深的情况,然后第 5 级成功完成,然后第 4 级成功完成,但随后第 3 级决定转到 "undo",它将执行 ROLLBACK TRANSACTION ProcSavePoint;
,这只会回滚第 5 个级别。这会让您处于糟糕的状态,因为目的是回滚到级别 3 开始时的状态。
使用唯一的保存点名称可以解决这个问题。
奇怪的是你没有使用 TRY
/ CATCH
结构。你真的应该。如果您有逻辑决定根据不是 SQL 服务器错误的特定条件取消操作,您仍然可以通过调用 RAISERROR()
立即转到 CATCH
来强制执行此操作堵塞。或者,如果您不想将其作为错误处理,除了 TRY
/ CATCH
.
[=69= 之外,您仍然可以执行 GOTO undo
方法]
我不相信 XACT_STATE()
可以在 TRY
/ CATCH
构造之外报告 -1
。
你为什么首先使用保存点?您是否遇到过外层可能会继续并最终 COMMIT
即使在子过程调用中发生错误的情况?
我最常使用的模板显示在我对 DBA.StackExchange 这个问题的回答中:Are we required to handle Transaction in C# Code as well as in Store procedure。该模板只是在开始时检查活动事务(类似于您的方法),但如果存在活动事务则不执行任何操作。因此,永远不会调用额外的 BEGIN TRAN
甚至 SAVE TRAN
,只有外部的稍后(即使它是应用程序代码)才会执行 COMMIT
或 ROLLBACK
。
只是为了指出这一点,因为它 看起来 就像你的代码和我在那个链接的答案中发布的代码之间的功能差异,但实际上不是:没有具体需要捕获 @@TRANCOUNT
的实际值,因为唯一的选项是 0
或 > 0
,并且除非 @@TRANCOUNT
在输入模板时已经 > 1,否则它将达到最大值ever get 无论如何都是 1(如果 Triggers and/or INSERT INTO ... EXEC
递增,即使存在活动事务,也可能是 2)。在任何一种情况下,我对 @InNestedTransaction
使用 BIT
变量在功能上/逻辑上等同于将 @@TRANCOUNT
存储在 INT
变量中,因为 SAVE TRAN
不会递增 @@TRANCOUNT
.
我不清楚我是否需要为我使用的每个 SP 使用不同的保存点名称 SAVE TRANSACTION
。
我可以一直使用例如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
希望我的问题很清楚。
从技术上讲,是的,您可以重复使用相同的保存点名称,它们将像多次调用 BEGIN TRAN
一样堆叠起来,每次调用 COMMIT
只会使计数器递减。意思是,如果你发出 SAVE TRANSACTION ProcSavePoint;
5 次,然后调用 ROLLBACK TRANSACTION ProcSavePoint;
2 次,你仍然会停留在第三次调用 SAVE TRAN
之后和调用它之前的状态第四次。
但是,此代码在几个层面上存在问题:
由于刚才提到的行为,在嵌套场景中,根据调用
GOTO Undo
的条件,如果您遇到调用嵌套过程 5 层深的情况,然后第 5 级成功完成,然后第 4 级成功完成,但随后第 3 级决定转到 "undo",它将执行ROLLBACK TRANSACTION ProcSavePoint;
,这只会回滚第 5 个级别。这会让您处于糟糕的状态,因为目的是回滚到级别 3 开始时的状态。使用唯一的保存点名称可以解决这个问题。
奇怪的是你没有使用
[=69= 之外,您仍然可以执行TRY
/CATCH
结构。你真的应该。如果您有逻辑决定根据不是 SQL 服务器错误的特定条件取消操作,您仍然可以通过调用RAISERROR()
立即转到CATCH
来强制执行此操作堵塞。或者,如果您不想将其作为错误处理,除了TRY
/CATCH
.GOTO undo
方法]我不相信
XACT_STATE()
可以在TRY
/CATCH
构造之外报告-1
。你为什么首先使用保存点?您是否遇到过外层可能会继续并最终
COMMIT
即使在子过程调用中发生错误的情况?我最常使用的模板显示在我对 DBA.StackExchange 这个问题的回答中:Are we required to handle Transaction in C# Code as well as in Store procedure。该模板只是在开始时检查活动事务(类似于您的方法),但如果存在活动事务则不执行任何操作。因此,永远不会调用额外的
BEGIN TRAN
甚至SAVE TRAN
,只有外部的稍后(即使它是应用程序代码)才会执行COMMIT
或ROLLBACK
。只是为了指出这一点,因为它 看起来 就像你的代码和我在那个链接的答案中发布的代码之间的功能差异,但实际上不是:没有具体需要捕获
@@TRANCOUNT
的实际值,因为唯一的选项是0
或> 0
,并且除非@@TRANCOUNT
在输入模板时已经 > 1,否则它将达到最大值ever get 无论如何都是 1(如果 Triggers and/orINSERT INTO ... EXEC
递增,即使存在活动事务,也可能是 2)。在任何一种情况下,我对@InNestedTransaction
使用BIT
变量在功能上/逻辑上等同于将@@TRANCOUNT
存储在INT
变量中,因为SAVE TRAN
不会递增@@TRANCOUNT
.