Sqlserver的Transaction做Rollback的時候要小心(轉載)
仔細研究了下,發現sql server裏面的explicit transaction(顯示事務)還是有點復雜的。以下是有些總結:
Commit transaction 會提交所有嵌套的transaction修改。但是如果嵌套的transaction裏面有rollback tran to save point, 那麽save point之後的部分會revert掉。
delete from dbo.numbertable begin tran out1 insert into dbo.numbertable values(1) insert into dbo.numbertable values(2) begin tran inn1 insert into dbo.numbertable values(3) insert into dbo.numbertable values(4) save tran inn1SavePoint insert into dbo.numbertable values(5) rollback tran inn1SavePoint commit tran inn1 commit tran out1
@@TRANCOUNT可以用來記錄當前session transaction的個數,對於嵌套的transaction來講,每次begin transaction都讓它加一,每次commit tran都會讓它減一。所以在語句裏面可以通過select @@TRANCOUNT 來檢查當前是否在一個transaction裏面。如果當前@@TRANCOUNT為0,那調用commit還是rollback都會出現語句錯誤。在嵌套的transaction裏面,rollback是很特殊的,它會直接把@@TRANCOUNT設置為0。
begin tran begin tran begintran print @@trancount rollback tran print @@trancount
對於嵌套的transaction來講,rollback的寫法是很特殊。如果嵌套,rollback transaction後面是不能帶transaction的name的,要帶也只能是最外面的transaction的name。Rollback會拋棄所有嵌套transaction在rollback語句之前的修改。不過Rollback之後的更新依然會提交就是了,原因在於:rollback之後,@@trancount為0,那麽rollback之後的語句就不屬於explicit transaction, 屬於autocmmit transaction了,自動提交。
delete from dbo.numbertable begin tran t1 insert into dbo.numbertable values(1) begin tran t2 insert into dbo.numbertable values(2) rollback tran print ‘after rollback in innert transaction, the transaction count is: ‘+cast(@@trancount, varchar(5)) insert into dbo.numbertable values(3) --commit tran select * from dbo.numbertable
存儲過程裏面也可以begin transaction,如果調用存儲過程的地方也begin transaction,那麽這種情況也屬於嵌套transaction,如果在存儲過程裏面rollback,得到的結果和上面一樣。但是有一點特殊的地方在與,執行存儲過程結束的時候會比較開始執行存儲過程的@@trancount和結束時候@@trancount的值,如果不一樣,Sqlserver會給出一個消息像“Transaction count after EXECUTE indicates that a COMMIT or ROLLBACK TRANSACTION statement is missing. Previous count = 1, current count = 0.”這個給出的消息並不會影響其後的執行。
CREATE PROCEDURE [dbo].[AddNumber] AS BEGIN begin tran insert into dbo.numbertable values(1) insert into dbo.numbertable values(2) insert into dbo.numbertable values(3) rollback tran END delete from dbo.numbertable begin tran out1 exec dbo.addnumber print @@trancount insert into dbo.numbertable values(3) select * from dbo.numbertable
如果在存儲過程裏面做rollback了,那到外面再做commit或者rollback都是沒有效果的並且會報錯,因為嵌套transaction內部的transaction一旦調用了rollback,@@trancount就為0了,在外面commit或rollback就會直接報錯。比如如下sp,我想像在最外面rollback,那就出錯了,因為sp裏面語句rollback了。最後表裏面始終會插入值3。
delete from dbo.numbertable begin tran out1 exec dbo.addnumber print @@trancount insert into dbo.numbertable values(3) rollback tran out1 select * from dbo.numbertable
所以對於嵌套的transaction來講,如果內部transaction一旦rollback,就會給外部的transaction留下一個大坑。為了解決這個為題,有兩種解決方案:
1.在外部的transaction裏面檢查@@trancount,如果這個值跟你代碼begin tran的時候一致,那說明內部transaction沒有rollback,那可以繼續commit或者rollback。
delete from dbo.numbertable begin tran t1 insert into dbo.numbertable values(1) begin transaction t2 insert into dbo.numbertable values(2) rollback tran if @@trancount = 1 begin insert into dbo.numbertable values(3) commit tran end
2.在所有的內部transaction裏面,只能commit,不能rollback。如果必須rollback,那怎麽辦?save point就可以派上用場了。比如sp改成這樣子:
ALTER PROCEDURE [dbo].[AddNumber] AS BEGIN begin tran save tran pp insert into dbo.numbertable values(1) insert into dbo.numbertable values(2) insert into dbo.numbertable values(3) rollback tran pp commit tran END begin tran out1 exec dbo.addnumber print @@trancount insert into dbo.numbertable values(3) commit tran out1
原文鏈接
Sqlserver的Transaction做Rollback的時候要小心(轉載)