作为在此演示,
如果我有一个内部存储过程,这总是没有错误回滚。
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调用内部,
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以外的任何值时,其他任何操作都不会发生。所以,一步一步,
BEGIN TRANSACTION
将@@TRANCOUNT
设置为1
。@@TRANCOUNT
1
调用内部。ROLLBACK TRANSACTION
将@@TRANCOUNT
重置为0
。@@TRANCOUNT
0
一起返回外部。@@TRANCOUNT
中的不匹配,因此会引发上述错误。所以我的问题是
我的理解正确吗?规范文档在哪里?
编写存储过程的最佳方法是什么,它可能希望回滚自己的更改,以便可以直接在自己的批处理中或从另一个事务中调用它?
发布于 2022-08-18 09:57:48
是的,你的理解是正确的。
在存储过程中,没有
ROLLBACK TRANSACTION
或transaction_name
的savepoint_name
语句将所有语句回滚到最外层的BEGIN TRANSACTION
。存储过程中的ROLLBACK TRANSACTION
语句在存储过程完成时使@@TRANCOUNT
具有与调用存储过程时的@@TRANCOUNT
值不同的值,从而产生一条信息性消息。此消息不影响后续处理。
我不知道为什么它说“不影响后续处理”,因为这是显然是假的:相反,它抛出一个严重性为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
https://stackoverflow.com/questions/73400364
复制相似问题