您的位置:首页 > 编程语言

关于事务的代码编写注意事项

2010-03-25 08:49 411 查看
以下内容为自己总结,望讨论更正

一、基础概念

SQL Server 数据库引擎将忽略内部事务的提交。根据最外部事务结束时采取的操作,将提交或者回滚内部事务。如果提交外部事务,也将提交内部嵌套事务。如果回滚外部事务,也将回滚所有内部事务,不管是否单独提交过内部事务。

嵌套事务的程序执行过程应该是:随着事务的执行,嵌套层数由一递增,之后逐渐递减到第一层。整个事务是否提交取决于最后一层是运行Commit(提交)还是Rollback(回滚)。

1、对 COMMIT TRANSACTION的每个调用都应用于最后执行的 BEGIN TRANSACTION。如果嵌套 BEGIN TRANSACTION 语句,那么 COMMIT 语句只应用于最后一个嵌套的事务,也就是在最内部的事务。即使嵌套事务内部的 COMMIT TRANSACTION transaction_name 语句引用外部事务的事务名称,该提交也只应用于最内部的事务。

2、ROLLBACK TRANSACTION 语句的 transaction_name 参数引用一组命名嵌套事务的内部事务是非法的,transaction_name 只能引用最外部事务的事务名称。如果在一组嵌套事务的任意级别执行使用外部事务名称的 ROLLBACK TRANSACTION transaction_name 语句,那么所有嵌套事务都将回滚。如果在一组嵌套事务的任意级别执行没有 transaction_name 参数的ROLLBACK TRANSACTION 语句,那么所有嵌套事务都将回滚,包括最外部事务。(此段意思表示:只允许嵌套层第一层时使用ROLLBACK回滚)

3、@@TRANCOUNT 函数记录当前事务的嵌套级别。每个 BEGIN TRANSACTION 语句使 @@TRANCOUNT 增加 1。每个 COMMIT TRANSACTION语句使 @@TRANCOUNT 减去 1。

A、没有事务名称的ROLLBACK TRANSACTION 语句将回滚所有嵌套事务,并使 @@TRANCOUNT 减小到 0。

B、使用一组嵌套事务中最外部事务的事务名称的 ROLLBACK TRANSACTION 将回滚所有嵌套事务,并使 @@TRANCOUNT 减小到 0。

C、在无法确定是否已经在事务中时,可以用 SELECT @@TRANCOUNT 确定 @@TRANCOUNT 是等于 1 还是大于 1。如果 @@TRANCOUNT 等于 0,则表明不在事务中。

注:以上内容摘自-- http://msdn.microsoft.com/zh-cn/library/ms189336.aspx

二、示例说明:

1、创建一个表格,用于插入数据的实验

create table TranTestTable(

T datetime

)

2、没有使用事务,程序运行会出错。但第一条语句还是插入了一条记录,第二条没有插入记录。

alter PROC TranTest2(@errorTag int output)

AS

BEGIN

insert into TranTestTable values(getdate())

insert into TranTestTable values('....')

END

3、见下面代码,存在两层事务的嵌套使用,TranTest1嵌套调用TranTest2,根据“一”中的1、2可以得出以下代码。调用TranTest1运行正常,没有插入任何记录。

alter PROC TranTest1(@errorTag int output)

AS

BEGIN

set @errorTag = 0

Begin transaction

declare @errorTag2 int

exec TranTest2 @errorTag2 output ----调用TranTest2存储过程

if @errorTag2<0

BEGIN

set @errorTag = -1

ROLLBACK TRANSACTION

RETURN

END

IF @@TRANCOUNT > 0

COMMIT TRANSACTION

END

--------- TranTest2代码------------

alter PROC TranTest2(@errorTag int output)

AS

BEGIN

set @errorTag = 0

Begin transaction

insert into TranTestTable values(getdate())

insert into TranTestTable values('....')

if @@error <> 0

BEGIN

set @errorTag = -1

COMMIT TRANSACTION

RETURN

END

IF @@TRANCOUNT > 0

COMMIT TRANSACTION

END

4、但TranTest2是一个独立的存储过程,也可能被直接嗲用(外层不嵌套事务),TranTest2被TranTest1调用和直接被调用的区别在于事务嵌套级别不同。直接被调用(不存在嵌套调用)时,此时TranTest2中就是事务的第一层,碰到异常情况应该运行ROLLBACK TRANSACTION使得整个事务回滚;被TranTest1调用(存在嵌套调用)时,TranTest2的层级(@@TRANCOUNT的值)为2,此时出现异常情况应该运行COMMIT TRANSACTION,将嵌套层数减1,并将事务交给上一层处理,最后这个事务是否提交取决于第一层最后是提交还是回滚;

为了使得TranTest2存储过程具有通用性,可以根据嵌套层数决定是调用COMMIT还是ROLLBACK。如果是大于1,说明上面还有事务包着,此时事务应该交给上一层处理。如果是1,则需要回滚整个事务,因为没有其他地方可以将事务回滚了。

alter PROC TranTest2(@errorTag int output)

AS

BEGIN

set @errorTag = 0

Begin transaction

insert into TranTestTable values(getdate())

insert into TranTestTable values('....')

if @@error <> 0

BEGIN

set @errorTag = -1

IF @@TRANCOUNT > 1

COMMIT TRANSACTION

ELSE

ROLLBACK TRANSACTION

RETURN

END

IF @@TRANCOUNT > 0

COMMIT TRANSACTION

END

5、最后将TranTest1像TranTest2一样改成具有通用性,因为TranTest1也不知道会被谁调用。

alter PROC TranTest1(@errorTag int output)

AS

BEGIN

set @errorTag = 0

Begin transaction

declare @errorTag2 int

exec TranTest2 @errorTag2 output

if @errorTag2<0

BEGIN

set @errorTag = -1

IF @@TRANCOUNT > 1

COMMIT TRANSACTION

ELSE

ROLLBACK TRANSACTION

RETURN

END

IF @@TRANCOUNT > 0

COMMIT TRANSACTION

END

三、总结整理

在存储过程中启动事务:

遵守一个原则,启动一个事务时@@TRAN COUNT的值和结束该层事务时的@@TRAN COUNT的值是相等的。在上一层的事务中是提交还是回滚,是根据内层返回的回滚提交标志决定的。

1、在事务的正常完成结束点。事务正常结束时@@TRAN COUNT减去1。

IF @@TRANCOUNT > 0

COMMIT TRANSACTION

A、如果外层没有嵌套事务,则@@TRANCOUNT=1正常提交;

B、如果外层嵌套了事务,则@@TRANCOUNT>1,运行COMMIT使得@ @TRANCOUNT-1,并且返回正确系统运行正常信息,事务的真正提交交给外层事务。

2、在事务中途要退出事务,并且返回错误标志(回滚标志)。

IF @@TRANCOUNT > 1

COMMIT TRANSACTION

ELSE

ROLLBACK TRANSACTION

RETURN

A、如果外层嵌套了事务,则满足IF @@TRANCOUNT > 1条件,运行COMMIT使得@@TRANCOUNT-1,并且返回错误信息,真正的回滚交给最上层事务(由上层根据这个错误信息决定回滚)。

B、如果外层没有嵌套事务,则@@TRANCOUNT=1,满足else,直接回滚;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: