【问题标题】:Relationship between Transactions in Nested Stored Procedures?嵌套存储过程中事务之间的关系?
【发布时间】:2012-02-02 20:26:57
【问题描述】:

我将交易放入我所有的“设置”程序中。没问题。一切正常。

在这种情况下,我需要一组程序来调用另一个程序,谢天谢地,只调用一次,否则可能会使事情变得更加复杂。

所以快乐的沐浴就是这样。

  • 我在 ProcA 并开始交易。
  • 它调用 ProcB 并启动一个事务。
  • ProcB 成功并提交。
  • ProcA 成功并提交。

但是,如果 ProcB 失败、回滚并重新抛出错误,会发生什么情况。它应该会导致 ProcA 回滚正确吗?

如果 ProcB 成功,提交,然后 ProcA 随后失败,并回滚...... ProcB 中发生的事情会被回滚吗?还是承诺了?

我需要这两者一起工作,要么都成功,要么失败并且都被回滚。确保发生这种情况的最佳方法是什么?

我正在使用 Microsoft SQL Server 2008 R2 (SP1)

注意:如果 ProcB 需要一个事务,因为它可以在没有 ProcA 包装的情况下被调用。从技术上讲,ProcA 并不总是调用 ProcB(取决于输入)。

【问题讨论】:

    标签: sql-server sql-server-2008 stored-procedures transactions


    【解决方案1】:

    这里有一个简单的演示来展示嵌套事务会发生什么:

    CREATE TABLE TranTest (Field1 INTEGER)
    
    BEGIN TRANSACTION
    SELECT @@TRANCOUNT -- 1 open transaction
    INSERT TranTest VALUES (1)
    
        BEGIN TRANSACTION
        SELECT @@TRANCOUNT -- 2 open transactions
        INSERT TranTest VALUES (2)
        ROLLBACK TRANSACTION -- this rolls back ALL transaction
    
    SELECT @@TRANCOUNT -- 0 open transactions (you may have expected 1?)
    SELECT * FROM TranTest -- No rows
    

    而不是上面的 ROLLBACK,如果您执行了 COMMIT TRANSACTION,则此实际操作除了递减 @@TRANCOUNT 之外什么都不做。因此,您需要提交外部事务(这会将两行都提交到表中),或者执行 ROLLBACK,这将导致没有行被提交到表中。

    这是关于嵌套事务的 MSDN 参考:http://msdn.microsoft.com/en-us/library/ms189336.aspx

    【讨论】:

      【解决方案2】:

      只需使用 XACT_ABORT ON,一切就绪。运行以下脚本并亲自查看:

      CREATE DATABASE ak_test;
      GO
      USE ak_test;
      GO
      CREATE TABLE dbo.a(i INT CONSTRAINT a_CannotInsertNegative CHECK(i>=0));
      GO
      CREATE TABLE dbo.b(i INT CONSTRAINT b_CannotInsertNegative CHECK(i>=0));
      GO
      
      CREATE PROCEDURE dbo.innerProc @i INT
      AS
      SET XACT_ABORT ON ;
      BEGIN TRAN
      INSERT b(i)VALUES(@i);
      COMMIT;
      GO
      
      CREATE PROCEDURE dbo.outerProc @i1 INT, @i2 INT, @i3 INT
      AS
      SET XACT_ABORT ON ;
      BEGIN TRAN
      INSERT a(i)VALUES(@i1);
      EXEC innerProc @i=@i2;
      INSERT a(i)VALUES(@i3);
      COMMIT;
      GO
      
      -- succeeds
      EXEC dbo.outerProc 1, 2, 3;
      SELECT * FROM dbo.a;
      SELECT * FROM dbo.b;
      GO
      -- inner proc fails
      EXEC dbo.outerProc 2, -3, 4;
      GO
      SELECT * FROM dbo.a;
      SELECT * FROM dbo.b;
      GO
      -- second insert in outer proc fails
      EXEC dbo.outerProc 3, 4, -5;
      GO
      SELECT * FROM dbo.a;
      SELECT * FROM dbo.b;
      

      【讨论】:

        【解决方案3】:

        我对交易持怀疑态度(一旦半小时内没人注意到这个交易在生产上一直处于开放状态......)所以我会像这样扭曲 可能 内部交易:

        CREATE PROCEDURE etcetc
        
        ...
        
        DECLARE @IsTransaction bit = 0
        
        IF @@trancount > 0
         BEGIN
            BEGIN TRANSACTION
            SET @IsTransaction = 1
         END
        
        ...
        
        IF @IsTransaction = 1
         BEGIN
            COMMIT
            --  or ROLLBACk, as necessary
         END
        

        所有事务处理(以及事务中发生的错误的处理)必须在启动事务的任何级别进行处理。

        (还有其他人是否注意到 BOL 实际上并没有说明当您向不是最外层事务的命名事务发出 ROLLBACK 时会发生什么?他们确实拼出了其他所有排列...)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-06-24
          • 1970-01-01
          • 1970-01-01
          • 2020-12-11
          • 2011-09-28
          相关资源
          最近更新 更多