首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >“嵌套”事务,为什么会发生这种情况,我如何避免它?

“嵌套”事务,为什么会发生这种情况,我如何避免它?
EN

Stack Overflow用户
提问于 2022-08-18 09:11:51
回答 1查看 79关注 0票数 1

作为在此演示

如果我有一个内部存储过程,这总是没有错误回滚。

代码语言:javascript
复制
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调用内部,

代码语言:javascript
复制
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,

代码语言:javascript
复制
EXEC [outer];

我知道这个错误

代码语言:javascript
复制
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以外的任何值时,其他任何操作都不会发生。

所以,一步一步,

  1. 外部-> BEGIN TRANSACTION@@TRANCOUNT设置为1
  2. 外部->用@@TRANCOUNT 1调用内部。
  3. 内部-> ROLLBACK TRANSACTION@@TRANCOUNT重置为0
  4. 内部->与@@TRANCOUNT 0一起返回外部。
  5. Server在调用内部之前和之后检测到@@TRANCOUNT中的不匹配,因此会引发上述错误。

所以我的问题是

我的理解正确吗?规范文档在哪里?

编写存储过程的最佳方法是什么,它可能希望回滚自己的更改,以便可以直接在自己的批处理中或从另一个事务中调用它?

EN

Stack Overflow用户

发布于 2022-08-18 09:57:48

是的,你的理解是正确的。

文件如下:

在存储过程中,没有ROLLBACK TRANSACTIONtransaction_namesavepoint_name语句将所有语句回滚到最外层的BEGIN TRANSACTION。存储过程中的ROLLBACK TRANSACTION语句在存储过程完成时使@@TRANCOUNT具有与调用存储过程时的@@TRANCOUNT值不同的值,从而产生一条信息性消息。此消息不影响后续处理。

我不知道为什么它说“不影响后续处理”,因为这是显然是假的:相反,它抛出一个严重性为16的错误,这个错误可以用BEGIN CATCH捕获。

如果您确实希望使用这样的嵌套事务,并且只能够部分回滚它们,那么它就会变得更加复杂。

如果没有交易,您必须有条件地开始一项事务。然后,您必须SAVE一个保存点。然后,每个回滚都必须有条件地回滚整个事务,或者只回滚保存点。

代码语言:javascript
复制
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

db<>fiddle

票数 2
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73400364

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档