SQL Server 2005/2008/2012中事务回滚的一个充分条件

SQL Server 2008中SQL应用系列–目录索引

  在SQL Server 2000中,我们一般使用RaiseError()来抛出错误交给应用程序来处理。看MSDN示例(%28v=sql.80%29.aspx),自从SQL Server 2005集成Try…Catch功能以后,我们使用时更加灵活,到了SQL Server 2012,更推出了强大的THROW,处理错误显得更为精简。本文对此作一个小小的展示。

  首先,我们假定两个基本表如下:

–创建两个测试表IF NOT OBJECT_ID(‘Score’) IS NULLDROP TABLE [Score]GOIF NOT OBJECT_ID(‘Student’) IS NULLDROP TABLE [Student]GOCREATE TABLE Student(stuid int NOT NULL PRIMARY KEY,stuName Nvarchar(20))CREATE TABLE Score(stuid int NOT NULL REFERENCES Student(stuid),–外键scoreValue int)GOINSERT INTO Student VALUES (101,’胡一刀’)INSERT INTO Student VALUES (102,’袁承志’)INSERT INTO Student VALUES (103,’陈家洛’)INSERT INTO student VALUES (104,’张三丰’)GOSELECT * FROM Student/*stuid stuName101 胡一刀102 袁承志103 陈家洛104 张三丰*/

  我们从一个最简单的例子入手:

例一:

/********* 调用运行时错误 ***************//********* 3w@live.cn 邀月***************/SET XACT_ABORT OFFBEGIN TRANINSERT INTO Score VALUES (101,80)INSERT INTO Score VALUES (102,87)INSERT INTO Score VALUES (107, 59) /* 外键错误 */—–SELECT 1/0 /* 除数为0错误 */INSERT INTO Score VALUES (103,100)INSERT INTO Score VALUES (104,99)COMMIT TRANGO

  先不看结果,我想问一下,该语句执行完毕后,Score表会插入几条记录?估计可能有人说是2条,有人说0条,也可能有人说4条。

  实际上,我希望是0条,但结果是4条! 

/*(1 row(s) affected)(1 row(s) affected)Msg 547, Level 16, State 0, Line 5The INSERT statement conflicted with the FOREIGN KEY constraint "FK__Score__stuid__01D345B0". The conflict occurred in database "testDb2", table "dbo.Student", column ‘stuid’.The statement has been terminated.(1 row(s) affected)(1 row(s) affected)*/SELECT * from Score/*stuid scoreValue101 80102 87103 100104 99*/

  我对这个结果也有点惊讶,我希望它出错回滚,于是修改:

例二:

/********* 调用运行时错误 ***************//********* 3w@live.cn 邀月***************/TRUNCATE table ScoreGOSET XACT_ABORT OFFBEGIN TRANINSERT INTO Score VALUES (101,80)INSERT INTO Score VALUES (102,87)INSERT INTO Score VALUES (107, 59) /* 外键错误 */—-SELECT 1/0–INSERT INTO Score VALUES (103,100)–INSERT INTO Score VALUES (104,99)PRINT ‘@@ERROR是:’+cast(@@ERROR as nvarchar(10))IF @@ERROR<>0ROLLBACK TRANELSECOMMIT TRANGO   我先提示一下大家,这个语句中的@@ERROR值是547,那么此时,Score表中有几条记录?

  答案是2条!

  可能有人开始摇头了,那么问题的关键在哪儿呢?对,就是这个“XACT_ABORT ”开关,查MSDN(),

官方解释:它用于指定当 Transact-SQL 语句出现运行时错误时,SQL Server 是否自动回滚到当前事务。当 SET XACT_ABORT 为 ON 时,如果执行 Transact-SQL 语句产生运行时错误,则整个事务将终止并回滚。当 SET XACT_ABORT 为 OFF 时,有时只回滚产生错误的 Transact-SQL 语句,而事务将继续进行处理。 如果错误很严重,,那么即使 SET XACT_ABORT 为 OFF,也可能回滚整个事务。 OFF 是默认设置。编译错误(如语法错误)不受 SET XACT_ABORT 的影响。对于大多数 OLE DB 访问接口(包括 SQL Server),必须将隐式或显示事务中的数据修改语句中的 XACT_ABORT 设置为 ON。 唯一不需要该选项的情况是在提供程序支持嵌套事务时。

  这里,红色的一句话是关键,那么“有时”究竟是指什么时候呢?查资料知:()

  大致分为以下四个级别:

    当等级SEVERITY为0-10时,为“信息性消息”,最轻。

    当等级为11-16时,为“用户可以纠正的数据库引擎错误”。如除数为零,等级为16

    当等级为17-19时,为“需要DBA注意的错误”。如内存不足、数据库引擎已到极限等。

    当等级为20-25时,为“致命错误或系统问题”。如硬件或软件损坏、完整性问题、媒体故障等。

  用户也可以自定义错误级别和类型。

  根据以上解释,我们最保险的方式是:Set XACT_ABORT ON。

  当然,使用Try…Catch在Set XACT_ABORT OFF时也能按照我们的意愿回滚。

例三:

/********* 使用Try Catch 构造一个错误记录 ***************//********* 3w@live.cn 邀月 ***************/SET XACT_ABORT OFFBEGIN TRYBEGIN TRANINSERT INTO Score VALUES (101,80)INSERT INTO Score VALUES (102,87)INSERT INTO Score VALUES (107, 59) /* 外键错误 */INSERT INTO Score VALUES (103,100)INSERT INTO Score VALUES (104,99)COMMIT TRANPRINT ‘事务提交’END TRYBEGIN CATCHROLLBACKPRINT ‘事务回滚’ –构造一个错误信息记录SELECT ERROR_NUMBER() AS 错误号,ERROR_SEVERITY() AS 错误等级,ERROR_STATE() as 错误状态,DB_ID() as 数据库ID,DB_NAME() as 数据库名称,ERROR_MESSAGE() as 错误信息;END CATCHGO  这个返回结果比较另类,它其实是一条拼凑起来的记录。

  记录并没有新增,因为Catch到错误而事务回滚了。

  使用RaiseError也可以把出错的信息抛给应用程序来处理。

要铭记在心;每天都是一年中最美好的日子

SQL Server 2005/2008/2012中事务回滚的一个充分条件

相关文章:

你感兴趣的文章:

标签云: