仔細研究了下,發現sql server裏面的explicit transaction(顯示事務)仍是有點複雜的。如下是有些總結:html
Commit transaction 會提交全部嵌套的transaction修改。可是若是嵌套的transaction裏面有rollback tran to save point, 那麼save point以後的部分會revert掉。sql
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。session
begin tran begin tran begin tran 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了,自動提交。spa
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.」這個給出的消息並不會影響其後的執行。code
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。server
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留下一個大坑。爲了解決這個爲題,有兩種解決方案:htm
1.在外部的transaction裏面檢查@@trancount,若是這個值跟你代碼begin tran的時候一致,那說明內部transaction沒有rollback,那能夠繼續commit或者rollback。blog
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
原文連接ci