--事务的原子性要求事务要么全部完成,要么全部不完成,不可能停滞在某个中间状态。
--然而,我的事务却没有“回滚”,为此还导致了异常数据的发生,为什么?
这是一个发生在我工作中的真实的案例,在用户问我的时候我当时也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倒是可以接受。
期待其他大神们给予指导。