先看一下SQL Server Online Help相关的说明

  1. Begin Transaction:标记一个显式本地事务的起始点。BEGIN TRANSACTION 使 @@TRANCOUNT 按 1 递增。
  2. Rollback Transaction: 将显式事务或隐性事务回滚到事务的起点或事务内的某个保存点。(嵌套事务时,该语句将所有内层事务回滚到最外面的 BEGIN TRANSACTION 语句。无论在哪种情况下,ROLLBACK TRANSACTION 都将 @@TRANCOUNT 系统函数减小为 0。ROLLBACK TRANSACTION savepoint_name 不减小 @@TRANCOUNT。)
  3. Commit Transaction:标志一个成功的隐性事务或显式事务的结束。如果 @@TRANCOUNT 为 1,COMMIT TRANSACTION 使得自从事务开始以来所执行的所有数据修改成为数据库的永久部分,释放事务所占用的资源,并将 @@TRANCOUNT 减少到 0。如果 @@TRANCOUNT 大于 1,则 COMMIT TRANSACTION 使 @@TRANCOUNT 按 1 递减并且事务将保持活动状态。

下面用代码进行解释,代码是根据Online Help Commit Transaction一节的代码修改而成,首先建立一个Table,然后开始三个Trasaction,中间人为触发一些错误,然后观察运行结果。

 1SQL Server嵌套事务探讨--Bad code
 2SQL Server嵌套事务探讨USE NORTHWIND;
 3SQL Server嵌套事务探讨--Create test table
 4SQL Server嵌套事务探讨IF Object_id(N'TestTran',N'U'IS NOT NULL
 5SQL Server嵌套事务探讨  DROP TABLE TESTTRAN;
 6SQL Server嵌套事务探讨
 7SQL Server嵌套事务探讨CREATE TABLE TESTTRAN (
 8SQL Server嵌套事务探讨  COLA INT   PRIMARY KEY,
 9SQL Server嵌套事务探讨  COLB CHAR(3));
10SQL Server嵌套事务探讨
11SQL Server嵌套事务探讨--Variable for keeping @@ERROR
12SQL Server嵌套事务探讨DECLARE  @_Error INT;
13SQL Server嵌套事务探讨SET @_Error = 0;
14SQL Server嵌套事务探讨
15SQL Server嵌套事务探讨--Begin 3 nested transaction
16SQL Server嵌套事务探讨BEGIN TRANSACTION OUTERTRAN;
17SQL Server嵌套事务探讨BEGIN TRANSACTION INNER1;
18SQL Server嵌套事务探讨BEGIN TRANSACTION INNER2;
19SQL Server嵌套事务探讨
20SQL Server嵌套事务探讨INSERT INTO TESTTRAN VALUES     (3,'ccc');--Inner2
21SQL Server嵌套事务探讨
22SQL Server嵌套事务探讨RAISERROR('Inner2 error'161)
23SQL Server嵌套事务探讨IF @@ERROR = 0
24SQL Server嵌套事务探讨    COMMIT TRANSACTION INNER2;
25SQL Server嵌套事务探讨ELSE
26SQL Server嵌套事务探讨    ROLLBACK TRANSACTION ;
27SQL Server嵌套事务探讨  
28SQL Server嵌套事务探讨INSERT INTO TESTTRAN VALUES     (2,'bbb');--Inner1
29SQL Server嵌套事务探讨
30SQL Server嵌套事务探讨IF @@ERROR = 0
31SQL Server嵌套事务探讨    COMMIT TRANSACTION INNER1;
32SQL Server嵌套事务探讨ELSE
33SQL Server嵌套事务探讨    ROLLBACK TRANSACTION ;
34SQL Server嵌套事务探讨  
35SQL Server嵌套事务探讨INSERT INTO TESTTRAN VALUES     (1,'aaa');--OuterTran
36SQL Server嵌套事务探讨
37SQL Server嵌套事务探讨--RAISERROR ('OuterTran error',16,1)
38SQL Server嵌套事务探讨                        
39SQL Server嵌套事务探讨IF @@ERROR = 0
40SQL Server嵌套事务探讨    COMMIT TRANSACTION OuterTran;
41SQL Server嵌套事务探讨ELSE
42SQL Server嵌套事务探讨    ROLLBACK TRANSACTION;    
43SQL Server嵌套事务探讨
44SQL Server嵌套事务探讨SELECT * FROM   TESTTRAN (NOLOCK);
45SQL Server嵌套事务探讨SELECT @@Trancount;


上述代码当内层事务发生错误时,并不能正常Rollback,因为Rollback@@Trancount变成了0,所以后面的Commit语句就找不到对应的Transaction了。解决问题的关键就是Rollback时要判断@@Trancount,当@@Trancount等于1时进行Rollback进行回滚,否则执行Commit@@Trancount-1,同时把@@Error传到外层事务交给外层事务处理。微软的原文是没有问题的,但是这种情况比较简单,我们一眼就能看出哪个是内层事务,哪个是外层事务,一共嵌套了几层,如果是SP调用呢?你不知道你的SP会被谁调用,也不知道会被嵌套几层。

下面看一下怎么处理内层事务的错误(何时Rollback, Commit及错误的传递)

 1--Good code
 2SQL Server嵌套事务探讨USE NORTHWIND;
 3SQL Server嵌套事务探讨
 4SQL Server嵌套事务探讨--Create test table
 5SQL Server嵌套事务探讨IF Object_id(N'TestTran',N'U'IS NOT NULL
 6SQL Server嵌套事务探讨  DROP TABLE TE
SQL Server嵌套事务探讨STTRAN;
 7SQL Server嵌套事务探讨
 8SQL Server嵌套事务探讨CREATE TABLE TESTTRAN (
 9SQL Server嵌套事务探讨  COLA INT   PRIMARY KEY,
10SQL Server嵌套事务探讨  COLB CHAR(3));
11SQL Server嵌套事务探讨
12SQL Server嵌套事务探讨--Variable for keeping @@ERROR
13SQL Server嵌套事务探讨DECLARE  @_Error INT;
14SQL Server嵌套事务探讨SET @_Error = 0;
15SQL Server嵌套事务探讨
16SQL Server嵌套事务探讨--Begin 3 nested transaction
17SQL Server嵌套事务探讨BEGIN TRANSACTION OUTERTRAN;
18SQL Server嵌套事务探讨BEGIN TRANSACTION INNER1;
19SQL Server嵌套事务探讨BEGIN TRANSACTION INNER2;
20SQL Server嵌套事务探讨
21SQL Server嵌套事务探讨INSERT INTO TESTTRAN VALUES     (3,'ccc');--Inner2
22SQL Server嵌套事务探讨
23SQL Server嵌套事务探讨--raiserror('Inner2 error', 16, 1)
24SQL Server嵌套事务探讨SET    @_Error = @@ERROR              
25SQL Server嵌套事务探讨IF    @_Error = 0
26SQL Server嵌套事务探讨    COMMIT TRAN INNER2;
27SQL Server嵌套事务探讨ELSE
28SQL Server嵌套事务探讨  IF @@TRANCOUNT > 1
29SQL Server嵌套事务探讨    COMMIT TRANSACTION INNER2;
30SQL Server嵌套事务探讨  ELSE
31SQL Server嵌套事务探讨       ROLLBACK TRANSACTION INNER2;
32SQL Server嵌套事务探讨  
33SQL Server嵌套事务探讨INSERT INTO TESTTRAN VALUES     (2,'bbb');--Inner1
34SQL Server嵌套事务探讨
35SQL Server嵌套事务探讨IF @_Error = 0
36SQL Server嵌套事务探讨    SET @_Error = @@ERROR                
37SQL Server嵌套事务探讨IF @_Error = 0
38SQL Server嵌套事务探讨    COMMIT TRAN INNER1;
39SQL Server嵌套事务探讨ELSE
40SQL Server嵌套事务探讨  IF @@TRANCOUNT > 1
41SQL Server嵌套事务探讨    COMMIT TRANSACTION INNER1;
42SQL Server嵌套事务探讨  ELSE
43SQL Server嵌套事务探讨    ROLLBACK TRANSACTION INNER1;
44SQL Server嵌套事务探讨  
45SQL Server嵌套事务探讨INSERT INTO TESTTRAN VALUES     (1,'aaa');--OuterTran
46SQL Server嵌套事务探讨
47SQL Server嵌套事务探讨RAISERROR ('OuterTran error',16,1)
48SQL Server嵌套事务探讨
49SQL Server嵌套事务探讨--    rollback transaction OuterTran
50SQL Server嵌套事务探讨SET @_Error = @_Error + @@ERROR
51SQL Server嵌套事务探讨                        
52SQL Server嵌套事务探讨IF @_Error = 0
53SQL Server嵌套事务探讨    COMMIT TRAN OUTERTRAN;
54SQL Server嵌套事务探讨ELSE
55SQL Server嵌套事务探讨    IF @@TRANCOUNT > 1
56SQL Server嵌套事务探讨        COMMIT TRANSACTION;
57SQL Server嵌套事务探讨    ELSE
58SQL Server嵌套事务探讨        ROLLBACK TRANSACTION OUTERTRAN;    
59SQL Server嵌套事务探讨
60SQL Server嵌套事务探讨SELECT * FROM   TESTTRAN (NOLOCK)


考虑到SP的调用,我们开发SP时应该在最后把@@ERROR返回供调用者检查。另外测试注意检查一下@@Trancount,有时结果看似正确,但是如果@@Trancount不等于0,说明我们的代码出了问题。

相关文章: