看過不少朋友的代碼,包括一些學了挺長時間的朋友,在對數據庫的處理當中從來不使用事務,或者有些雖然使用了事務處理,但並沒有達到他們所預期的效果。實際上,真正要做一個商業化的程序,對數據的事務處理是必不可少的。下面就一些事務處理的問題談一下我的看法。
什麼叫事務,這裡我就不祥述了,感興趣的朋友可以自己看一下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