如果我有一个内部存储过程,它总是会回滚而不会出现错误。
CREATE PROCEDURE [inner] AS BEGIN
SET XACT_ABORT, NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION;
IF (1 + 1 = 2) BEGIN
ROLLBACK TRANSACTION;
END ELSE BEGIN
COMMIT TRANSACTION;
END
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 BEGIN
ROLLBACK TRANSACTION;
END
END CATCH
END
以及一个调用内部 SP 的外部 SP,
CREATE PROCEDURE [outer] AS BEGIN
SET XACT_ABORT, NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION;
EXEC [inner];
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 BEGIN
ROLLBACK TRANSACTION;
END
;THROW;
END CATCH
END
然后我调用外部 SP,
EXEC [outer];
我收到此错误
Msg 266 Level 16 State 2 Line 0
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements.
Previous count = 1, current count = 0.
现在,最初这并不是我所期望的。
我认为正在发生的事情,
公理:
BEGIN TRANSACTION
始终将@@TRANCOUNT
加 1;COMMIT TRANSACTION
始终将@@TRANCOUNT
减 1;ROLLBACK TRANSACTION
始终将@@TRANCOUNT
重置为 0;- 没有嵌套事务!
- 当
@@TRANCOUNT
更改为除 0 之外的任何值时,不会发生任何其他情况。
所以,一步一步,
- [outer] ->
BEGIN TRANSACTION
将@@TRANCOUNT
设置为1
。 - [outer] -> 使用
@@TRANCOUNT
1
调用 [inner]。 - [inner] ->
ROLLBACK TRANSACTION
将@@TRANCOUNT
重置为0
。 - [inner] -> 使用
@@TRANCOUNT
0
返回到 [outer]。 - SQL Server 在调用 [inner] 之前和之后检测到
@@TRANCOUNT
不匹配,并且, 因此会引发上述错误。
所以,我的问题是,
我的理解正确吗?规范文档在哪里?
编写可能想要回滚自己的更改的存储过程的最佳方法是什么,以便可以在自己的批处理中或从另一个事务中直接调用它?
最佳答案
是的,您的理解是正确的。
In stored procedures,
ROLLBACK TRANSACTION
statements without asavepoint_name
ortransaction_name
roll back all statements to the outermostBEGIN TRANSACTION
. AROLLBACK TRANSACTION
statement in a stored procedure that causes@@TRANCOUNT
to have a different value when the stored procedure completes than the@@TRANCOUNT
value when the stored procedure was called produces an informational message. This message does not affect subsequent processing.
我不知道为什么它说“不影响后续处理”,因为那是 demonstrably false :相反,它会抛出严重性为 16 的错误,可以使用 BEGIN CATCH
捕获该错误。
如果您确实想使用这样的嵌套事务,并且只能部分回滚它们,那么它会变得更加复杂。
如果没有,您必须有条件地开始交易。然后你必须SAVE
一个保存点。然后每次回滚必须有条件地回滚整个事务或仅回滚保存点
CREATE PROCEDURE [inner] AS BEGIN
SET XACT_ABORT, NOCOUNT ON;
BEGIN TRY
DECLARE @tranCount int = @@TRANCOUNT;
IF @tranCount = 0
BEGIN TRANSACTION;
SAVE TRANSACTION innerSave;
IF (1 + 1 = 2) BEGIN
IF @tranCount = 0
ROLLBACK;
ELSE
ROLLBACK TRANSACTION innerSave;
END ELSE BEGIN
COMMIT TRANSACTION;
END
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 BEGIN
IF @tranCount = 0
ROLLBACK;
ELSE
ROLLBACK TRANSACTION innerSave;
END
END CATCH
END
关于sql-server - "Nested"交易,为什么会出现这种情况,如何避免?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73400364/