--事務的原子性要求事務要麼全部完成,要麼全部不完成,不可能停滯在某個中間狀態。--然而,我的事務卻沒有“回滾”,為此還導致了異常數據的發生,為什麼? 這是一個發生在我工作中的真實的案例,在用戶問我的時候我當時也SB了,在我理解了這背後的原理後,我雖然接受了SQL Server在某些場景下不回滾的設 ...
--事務的原子性要求事務要麼全部完成,要麼全部不完成,不可能停滯在某個中間狀態。
--然而,我的事務卻沒有“回滾”,為此還導致了異常數據的發生,為什麼?
這是一個發生在我工作中的真實的案例,在用戶問我的時候我當時也SB了,
在我理解了這背後的原理後,我雖然接受了SQL Server在某些場景下不回滾的設計使然,但不得不吐槽,這個功能真不爽!
下麵我利用一個樣例來描述這個問題:
--創建test1表 CREATE TABLE [dbo].[test1]( [id] [int] NOT NULL, [testname] [varchar](10) NULL ) ON [PRIMARY] GO
現在執行一個事務,事務中包含兩個insert操作,其中第一個insert操作的testname欄位超過了最大長度10
--顯示執行一個事務,插入兩行數據,其中第一行的testname欄位超過了最大長度 BEGIN TRANSACTION; --SELECT 1 --FROM test; INSERT INTO [dbo].[test1] ([id], [testname] ) VALUES (1, '123456789101' ); INSERT INTO [dbo].[test1] ([id], [testname] ) VALUES (888, '12345' ); COMMIT TRANSACTION;
如預料的一樣,SQL Server在執行第一條語句時報錯
“按理”說來,這個事務執行會失敗,第二條插入語句會回滾,但實際結果卻是:
select * from [dbo].[test1]
在某些場景下,這會導致異常數據的發生。
為什麼會這樣呢??
據MSDN,預設情況,SQL Server並不會回滾事務,即使事務中的某個語句報錯,事務還是會繼續執行下去,除非非常嚴重的錯誤(serverity level is greater or equals 16)。
這是由資料庫選項XACT_ABORT決定的,預設XACT_ABORT為OFF,
When SET XACT_ABORT is OFF, in some cases only the Transact-SQL statement that raised the error is rolled back and the transaction continues processing. Depending upon the severity of the error, the entire transaction may be rolled back even when SET XACT_ABORT is OFF. OFF is the default setting.
如果想規避這個問題,微軟告訴我們需要把XACT_ABORT設置為On,這樣一個事務中任務一個語句報錯都會導致整個事務回滾。
SET XACT_ABORT ON
當然,你也可以使用try、catch人工捕獲錯誤以便進行回滾或者在程式中使用事務。
我在想為什麼會有這麼DT的設計呢?
我們學事務的第一堂課裡面就講了事務的原子性要求:事務要麼全部執行,要麼全部失敗,不會存在中間狀態。
但SQL Server為什麼預設不回滾呢?百思不得其解。
求助MSDN時鄒大俠給的一個解釋雖然有道理(謝謝鄒大俠),但還是覺得不夠完美,因為即使是為了能夠讓開發人員自己來控制事務的狀態,也不應該把XACT_ABORT預設設置為OFF,相反,如果設置為On倒是可以接受。
期待其他大神們給予指導。