为什么在发生错误时执行部分事务?

时间:2014-03-19 15:32:10

标签: sql-server transactions batch-processing

我有这个交易:

BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT

BEGIN TRANSACTION

delete from Versions;
insert into Versions(Version) VALUES('1.0.0.0');
GO

ALTER TABLE dbo.MyTable ADD
    MyNewColumn varchar(1000) NULL

ALTER TABLE dbo.MyTable SET (LOCK_ESCALATION = TABLE)
GO
COMMIT

第一次版本表是空的,在MyTable中我有“MyNewColumn”列,所以当我尝试添加新列时,我收到一个错误。但是,当我执行脚本时,表版本有一条带有数据库版本的记录。

为什么呢?如果我正在使用一个事务而且我只有一个提交,那么我认为任何语句都不会在数据库中进行更改。

我希望如果其中一个语句失败,任何语句都无法更改数据库。

P.D。:我知道在尝试添加列之前我可以检查列是否存在,但我想知道为什么事务的工作方式与我期望的一样。

感谢。

1 个答案:

答案 0 :(得分:1)

忽略设置位,您的脚本:

  • 启动交易
  • 执行删除
  • 执行插入
  • 运行一些生成错误的代码
  • 执行COMMIT

因此,当然,这些初步变化已经应用。您可以设置XACT_ABORT,这将导致生成错误的回滚事务,但您仍需要非常小心:

create table T(ID int)
go
set xact_abort on
go
begin transaction
go
insert into T(ID) values (1)
go
alter table T add ID int null
go
insert into T(ID) values (2)
go
commit
go
select * from T

制作以下消息:

(1 row(s) affected)
Msg 2705, Level 16, State 4, Line 1
Column names in each table must be unique. Column name 'ID' in table 'T'
is specified more than once.

(1 row(s) affected)
Msg 3902, Level 16, State 1, Line 1
The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

(1 row(s) affected)

表中仍有一行包含2。为什么?因为错误通过回滚终止了事务,但是在没有显式事务存在的情况下运行了下一批(包含insert into T(ID) values (2))。除了一些手动步骤,例如:

除了批次的错误处理之外,你可以获得的帮助不大。
create table T(ID int)
go
set xact_abort on
go
begin transaction
go
insert into T(ID) values (1)
go
alter table T add ID int null
go
if @@TRANCOUNT = 0 goto errored
insert into T(ID) values (2)
errored:
go
if @@TRANCOUNT = 0 goto errored
commit
errored:
go
select * from T

并且您必须在每个包含错误的批次中执行相同操作。