转载自:http://www.cnblogs.com/kymo/archive/2008/05/14/1194161.html
先看一下SQL Server Online Help相关的说明
- Begin Transaction
:标记一个显式本地事务的起始点。BEGIN TRANSACTION 使 @@TRANCOUNT 按 1 递增。
- Rollback Transaction
:
将显式事务或隐性事务回滚到事务的起点或事务内的某个保存点。(嵌套事务时,该语句将所有内层事务回滚到最外面的 BEGIN TRANSACTION
语句。无论在哪种情况下,ROLLBACK TRANSACTION 都将 @@TRANCOUNT 系统函数减小为 0。ROLLBACK
TRANSACTION savepoint_name 不减小 @@TRANCOUNT。)
- Commit Transaction
:
标志一个成功的隐性事务或显式事务的结束。如果 @@TRANCOUNT 为 1,COMMIT TRANSACTION
使得自从事务开始以来所执行的所有数据修改成为数据库的永久部分,释放事务所占用的资源,并将 @@TRANCOUNT 减少到 0。如果
@@TRANCOUNT 大于 1,则 COMMIT TRANSACTION 使 @@TRANCOUNT 按 1 递减并且事务将保持活动状态。
下面用代码进行解释,代码是根据
Online Help Commit Transaction
一节的代码修改而成,首先建立一个
Table
,然后开始三个
Trasaction
,中间人为触发一些错误,然后观察运行结果。
1
--
Bad code
2
USE
NORTHWIND;
3
--
Create test table
4
IF
Object_id
(N
'
TestTran
'
,N
'
U
'
)
IS
NOT
NULL
5
DROP
TABLE
TESTTRAN;
6
7
CREATE
TABLE
TESTTRAN (
8
COLA
INT
PRIMARY
KEY
,
9
COLB
CHAR
(
3
));
10
11
--
Variable for keeping @@ERROR
12
DECLARE
@_Error
INT
;
13
SET
@_Error
=
0
;
14
15
--
Begin 3 nested transaction
16
BEGIN
TRANSACTION
OUTERTRAN;
17
BEGIN
TRANSACTION
INNER1;
18
BEGIN
TRANSACTION
INNER2;
19
20
INSERT
INTO
TESTTRAN
VALUES
(
3
,
'
ccc
'
);
--
Inner2
21
22
RAISERROR
(
'
Inner2 error
'
,
16
,
1
)
23
IF
@@ERROR
=
0
24
COMMIT
TRANSACTION
INNER2;
25
ELSE
26
ROLLBACK
TRANSACTION
;
27
28
INSERT
INTO
TESTTRAN
VALUES
(
2
,
'
bbb
'
);
--
Inner1
29
30
IF
@@ERROR
=
0
31
COMMIT
TRANSACTION
INNER1;
32
ELSE
33
ROLLBACK
TRANSACTION
;
34
35
INSERT
INTO
TESTTRAN
VALUES
(
1
,
'
aaa
'
);
--
OuterTran
36
37
--
RAISERROR ('OuterTran error',16,1)
38
39
IF
@@ERROR
=
0
40
COMMIT
TRANSACTION
OuterTran;
41
ELSE
42
ROLLBACK
TRANSACTION
;
43
44
SELECT
*
FROM
TESTTRAN (NOLOCK);
45
SELECT
@@Trancount
;
上述代码当内层事务发生错误时,并不能正常
Rollback
,因为
Rollback
把
@@Trancount
变成了
0
,所以后面的
Commit
语句就找不到对应的
Transaction
了。解决问题的关键就是
Rollback
时要判断
@@Trancount
,当
@@Trancount
等于
1
时进行
Rollback
进行回滚,否则执行
Commit
把
@@Trancount-1
,同时把
@@Error
传到外层事务交给外层事务处理。
微软的原文是没有问题的,但是这种情况比较简单,我们一眼就能看出哪个是内层事务,哪个是外层事务,一共嵌套了几层,如果是
SP
调用呢?你不知道你的
SP
会被谁调用,也不知道会被嵌套几层。
下面看一下怎么处理内层事务的错误(何时Rollback, Commit及错误的传递)
1
--
Good code
2
USE
NORTHWIND;
3
4
--
Create test table
5
IF
Object_id
(N
'
TestTran
'
,N
'
U
'
)
IS
NOT
NULL
6
DROP
TABLE
TE
STTRAN;
7
8
CREATE
TABLE
TESTTRAN (
9
COLA
INT
PRIMARY
KEY
,
10
COLB
CHAR
(
3
));
11
12
--
Variable for keeping @@ERROR
13
DECLARE
@_Error
INT
;
14
SET
@_Error
=
0
;
15
16
--
Begin 3 nested transaction
17
BEGIN
TRANSACTION
OUTERTRAN;
18
BEGIN
TRANSACTION
INNER1;
19
BEGIN
TRANSACTION
INNER2;
20
21
INSERT
INTO
TESTTRAN
VALUES
(
3
,
'
ccc
'
);
--
Inner2
22
23
--
raiserror('Inner2 error', 16, 1)
24
SET
@_Error
=
@@ERROR
25
IF
@_Error
=
0
26
COMMIT
TRAN
INNER2;
27
ELSE
28
IF
@@TRANCOUNT
>
1
29
COMMIT
TRANSACTION
INNER2;
30
ELSE
31
ROLLBACK
TRANSACTION
INNER2;
32
33
INSERT
INTO
TESTTRAN
VALUES
(
2
,
'
bbb
'
);
--
Inner1
34
35
IF
@_Error
=
0
36
SET
@_Error
=
@@ERROR
37
IF
@_Error
=
0
38
COMMIT
TRAN
INNER1;
39
ELSE
40
IF
@@TRANCOUNT
>
1
41
COMMIT
TRANSACTION
INNER1;
42
ELSE
43
ROLLBACK
TRANSACTION
INNER1;
44
45
INSERT
INTO
TESTTRAN
VALUES
(
1
,
'
aaa
'
);
--
OuterTran
46
47
RAISERROR
(
'
OuterTran error
'
,
16
,
1
)
48
49
--
rollback transaction OuterTran
50
SET
@_Error
=
@_Error
+
@@ERROR
51
52
IF
@_Error
=
0
53
COMMIT
TRAN
OUTERTRAN;
54
ELSE
55
IF
@@TRANCOUNT
>
1
56
COMMIT
TRANSACTION
;
57
ELSE
58
ROLLBACK
TRANSACTION
OUTERTRAN;
59
60
SELECT
*
FROM
TESTTRAN (NOLOCK)
考虑到SP的调用,我们开发SP时应该在最后把@@ERROR返回供调用者检查。另外测试注意检查一下@@Trancount,有时结果看似正确,但是如果@@Trancount不等于0,说明我们的代码出了问题。