看过不少朋友的代码,包括一些学了挺长时间的朋友,在对数据库的处理当中从来不使用事务,或者有些虽然使用了事务处理,但并没有达到他们所预期的效果。实际上,真正要做一个商业化的程序,对数据的事务处理是必不可少的。下面就一些事务处理的问题谈一下我的看法。
什么叫事务,这里我就不祥述了,感兴趣的朋友可以自己看一下sql server的book online或是msdn。我举一个浅显的例子吧,比如说你要把一个form中的数据同时向两个相关联的表中插入,如果不使用事务,那就可能出现这用情况,第一个表中成功更新后,突然数据库出现意外状况,第二个表的操作没有完成,这样的话有什么结果也就不用说了。要避免这种情况,就应该使用事务,它的作用就是要不两个表都操作成功,要不都失败,换句话说,就是保持数据的一致性。具体做法是这样,在begin trans之后的所有操作只有到commit trans的时候才真正执行,如果其中有一部分操作失败,那么就用rollback trans恢复整个事务,是数据保持所有操作进行以前的状态。但这里需要注意的一个问题是,如果有一部分操作失败,但不是致命错误,那么数据库不会停在那儿,而是继续下一条操作,直到执行到commit trans , 系统会执行那些已经成功的操作,这也就是为什么有些程序尽管用了事务但还是出现只有部分数据被更新的情况,而不是预想的能够保持数据的一致性。所以正确的做法应该是在每一个对数据的操作后,都应该检查一下@@error的值,如果出错则转向错误处理部分,该部分负责执行rollback trans ,恢复整个事务。
看下面两个表,这是一个bbs的注册用户表以及它的相关表。
if exists(select * from sysobjects where ID = object_id("BBSUser"))
drop table BBSUser
go
create table BBSUser
(
ID int Primary key identity not null ,
UserName varchar(20) default "" not null ,
Password varchar(10) default "" not null ,
UserType tinyint default 0 not null , --用户类型,1为斑竹
Email varchar(100) default "" not null ,
HomePage varchar(100) default "" not null ,
ICQ varchar(20) default "" not null ,
Signature varchar(255) default "" not null , --签名
Point int default 0 not null , --用户积分
)
go
if exists(select * from sysobjects where ID = object_id("BBSUserAction"))
drop table BBSUserAction
go
create table BBSUserAction
(
id int Primary key identity not null ,
UserID int default 0 not null , --用户ID
SignTime datetime default getdate() not null , --登录时间
IP varchar(15) default "" not null , --登录IP
)
go
下面这个存储过程要完成这样一个任务,就是用户在注册完后把数据插入这两个表,并且第二表中的UserID的值是插入第一个表中的自动增加型字段ID,这里就用到一个技巧,用到了系统变量@@identity,它的值就是最后一次插入操作中的identity型字段的值。这个存储过程中就用到了事务,请仔细看一下代码吧,我就不多说了。
if exists (select * from sysobjects where ID = object_id("up_RegisterUser"))
drop proc up_RegisterUser
go
create proc up_RegisterUser
@a_strUserName varchar(20) , @a_strPassword varchar(10) , @a_strEmail varchar(100) ,
@a_strHomepage varchar(100) , @a_strICQ varchar(20) , @a_strSignature varchar(255) ,
@a_strIP varchar(15)
as
declare @m_strUserID int
/*因为要对两个表进行操作,所以放到事务当中*/
begin tran --事务开始
insert into BBSUser values --更新BBSUser表
(
@a_strUserName , @a_strPassword , 0 , @a_strEmail ,
@a_strHomepage , @a_strICQ , @a_strSignature , 0
)
if (@@error <> 0) goto On_Error --如果操作失败,转向错误处理
select @m_strUserID = @@identity --保存刚插入的ID号
insert into BBSUserAction values --更新BBSUserAction表
(
@m_strUserID , getdate() , @a_strIP
)
if (@@error <> 0) goto On_Error --如果操作失败,转向错误处理
commit tran --事务结束
return (0) --返回成功码
On_Error: --错误处理
rollback tran --恢复事务
return (-1) --返回错误码
go