SQL Server中的CREATE TABLE语句是否不受ROLLBACK的影响?

Ata*_*rio 8 sql-server transactions rollback create-table

我有一个我每周"发布"的数据库,这意味着我发布了一个人们可以开始的备份,并且我发布了一个他们可以用来从上周升级的更新脚本(这样他们就可以尽可能地保留他们当前的数据).当然,此脚本包含大量的DDL的- CREATE TABLE,ALTER TABLE等等.它的基本结构是这样的:

/*

HOW TO USE THIS SCRIPT

1.  Run it against your existing DB
2.  Check whether there were any errors
3.  If there were, issue a rollback by highlighting this:
        ROLLBACK
    and executing it
4.  If there weren't, issue a commit by highlighting this:
        COMMIT
    and executing it
5.  !!! Not doing either of these will leave a transaction open, which will
    probably cause all further queries to time out till you do !!!

*/

SET XACT_ABORT ON;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

BEGIN TRANSACTION;
GO

-- Boilerplate checking and whatnot goes here
GO

-- Guts of operation, part 1
GO

-- Guts of operation, part 2
GO

-- Guts of operation, part 3
GO

-- . . .
GO

-- Guts of operation, part N
GO

-- Boilerplate cleanup stuff here
GO
Run Code Online (Sandbox Code Playgroud)

你会注意到我打开了交易并告诉他们要根据发生的事情手动完成交易.我宁愿这是自动的,但是对它绝望,因为整个事情总是一系列的几个批次,当然,TRY块不能跨越批次.因而最近加入了SET XACT_ABORT ON;轻微的缓解疼痛.无论如何,我自己尝试了这个,因为没有它,它对情景没有任何影响.无论如何.

最近,其中一个脚本具有创建表和其他语句的语句,以向现有表添加检查约束.我的一个用户运行了脚本并在约束上遇到错误; 事实证明他有预先存在的违反约束的数据.好的,没问题,做一个ROLLBACK.对,不需要做一个,XACT_ABORT已经做到了.关闭并修复数据行...完成.现在再试一次!呃...什么?这次约束没有错误,但它在CREATE TABLE语句上出错,说表已经存在......!咦?它不回滚吗?

我们最终从备份中恢复并重新修复数据并重新运行.但这既不是在这里也不是在那里.

所以,亲爱的读者: 这些表的创建是如何在交易回滚中幸存的?我怎么能不让他们?


编辑:好的,这是一个你可以运行的例子.

USE tempdb;
GO

CREATE DATABASE example;
GO

USE example;
GO

CREATE TABLE foo (a INT);
GO

INSERT INTO foo
VALUES (100);
GO

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

BEGIN TRANSACTION;
GO

ALTER TABLE foo ADD CHECK (a < 10);-- Gives error "The ALTER TABLE statement conflicted with the CHECK constraint…", as expected
GO

CREATE TABLE bar (b INT);
GO

ROLLBACK;-- Gives error "The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION."  Huh?  Where did our transaction go?
GO

SELECT *
FROM bar;-- Gives no error.  Table still exists!  NOT expected!
GO

USE tempdb;
GO

DROP DATABASE example;
Run Code Online (Sandbox Code Playgroud)

usr*_*usr 5

SQL Server有一个不幸的错误处理行为.根据具体的错误,它有时会中止语句,批处理,事务和连接,或者某些,或者没有.这是一个非常容易出错的模型.

在您的情况下,批次和事务因中止而中止XACT_ABORT.但是GO是批处理分隔符.你有多个批次.后来的批次继续运行.

使用一个批次,或稍后批次检查上一批次是否运行(可能通过检查@@TRANCOUNT).

(SQL Server的一个更好的模型是回滚事务但保持打开并使所有未来的语句都失败.这将使事务中包含的其余脚本不会泄漏.SQL Server没有这样的功能.)