如何使用try catch管理嵌套事务

时间:2012-12-27 15:38:53

标签: sql-server try-catch sqltransaction savepoints nested-transactions

--Drop Table Tab1

Begin Transaction TR1; 
Save Transaction TR1;
    Create Table Tab1(f1 decimal(10,0));
    Begin Transaction TR2
    Save Transaction TR2
        insert into Tab1 values(1);
        Begin Transaction TR3;
        Save Transaction TR3;
            insert into Tab1 values(2);
            Begin Try 
                insert into Tab1 values('OK');
                Commit Transaction TR3;
            END TRY
            BEGIN Catch
                print 'catch'
                RollBack Transaction TR3;
            End Catch

        insert into Tab1 values(3);
        Commit Transaction TR2
    insert into Tab1 values(4);
Commit Transaction TR1;
--Commit Transaction;
select * from Tab1;
Drop Table Tab1

Select @@TRANCount

错误发生:

  

Msg 3931,Level 16,State 1,Line 17   当前事务无法提交,也无法回滚到保存点。回滚整个交易。

如何处理。

1 个答案:

答案 0 :(得分:3)

当引发某些类型的错误时,您无法回滚到保存点。请参阅Martin Smith'sRollback transaction to savepoint on failing ALTER TABLE … ADD CONSTRAINT回复。您检测到这种情况的方法是测试Xact_state()

但是,您的问题有些不同,因为您还尝试使用嵌套事务。嵌套事务在SQL中并不像我们期望的那样真正起作用。

例如,Cannot roll back TR2. No transaction or savepoint of that name was found.

失败
    BEGIN TRANSACTION TR1; 
    BEGIN TRANSACTION TR2
    ROLLBACK TRANSACTION TR2
    COMMIT Transaction TR1

来自Nesting Transactions

  • SQL Server数据库引擎

  • 会忽略提交内部事务
  • ROLLBACK TRANSACTION语句的transaction_name参数引用一组命名嵌套事务的内部事务是不合法的。 transaction_name只能引用最外层事务的事务名称

Paul S. Randal在A SQL Server DBA myth a day: (26/30) nested transactions are real

中进一步探讨了这一点

您可以做的最好的事情是使用保存点,并检查捕获中的Xact_state以及最后。

BEGIN TRANSACTION tr1; 

SAVE TRANSACTION tr2; 

CREATE TABLE tab1 
  ( 
     f1 DECIMAL(10, 0) 
  ); 

SAVE TRANSACTION tr3 

INSERT INTO tab1 
VALUES     (1); 

SAVE TRANSACTION tr4; 

INSERT INTO tab1 
VALUES     (2); 

BEGIN try 
    -- change the order of the follwoing two lines around to see the difference
    INSERT INTO tab1 VALUES (1 / 0); --Results in a rollback to savepoint
    INSERT INTO tab1 VALUES ('OK');  --Results in a complete rollback

    COMMIT TRANSACTION tr4; 
END try 

BEGIN catch 
    IF Xact_state() = -1 
      BEGIN 
          PRINT 'rollback transaction no other work can be done' 

          ROLLBACK TRANSACTION; 
      END 
    ELSE 
      BEGIN 
          PRINT 'rollback to savepoint' 

          ROLLBACK TRANSACTION tr4 
      END 
END catch 

IF Xact_state() > 0 
  BEGIN 
      INSERT INTO tab1 
      VALUES     (3); 

      INSERT INTO tab1 
      VALUES     (4); 

      COMMIT TRANSACTION tr1; 

      SELECT * 
      FROM   tab1; 

      DROP TABLE tab1 
  END