sqlserver 为什么事务失败没有回滚

初学数据库便知道,事务应该有原子性:要求事务要么全部完成,要么全部不完成,不能停滞在某个中间状态。然而,在下面的例子中事务却没有完全“回滚”,导致了异常数据的发生,为什么?

 

一、 问题复现

CREATE TABLE [dbo].[test1]([id] [int] NOT NULL,[testname] [varchar](10) NULL) ON [PRIMARY]

现在执行一个事务,事务中包含两个insert操作,其中第一个insert操作的testname字段超过了最大长度10

BEGIN TRANSACTION;
INSERT INTO [test1] VALUES (1,'123456789101');
INSERT INTO [test1] VALUES (888,'12345');
COMMIT TRANSACTION;

如预料的一样,SQL Server在执行第一条语句时报错

image

“按理”说来,这个事务执行会失败,第二条插入语句会回滚,但实际结果却是:

select * from [dbo].[test1];

image

在某些场景下,这会导致异常数据的发生。

二、 原因分析

根据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人工捕获错误以便回滚或者在程序中使用事务。

三、 为什么会有这么奇葩的设计呢?

SQL Server为什么默认不完全回滚呢?百思不得其解。求助MSDN时邹大侠给的一个解释虽然有道理(谢谢邹大侠),但还是觉得不够完美,因为即使是为了能够让开发人员自己来控制事务的状态,也不应该把XACT_ABORT默认设置为OFF,相反,如果设置为on倒是可以接受。

image

发布了218 篇原创文章 · 获赞 29 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/Hehuyi_In/article/details/103229039