我有一个每周发布一次的数据库,这意味着我发布了一个备份人员可以从那里开始,并且发布了一个更新脚本,他们可以使用该脚本从上周的版本进行升级(以便他们可以尽可能多地保留其当前数据)。该脚本当然包含许多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
您会注意到,我让交易处于打开状态,并告诉他们要根据发生的情况手动完成交易。我宁愿让它自动执行,但由于整个事情总是由几个批次组成的长序列而使它绝望,所以
TRY
块当然不能跨越多个批次。因此,最近添加了SET XACT_ABORT ON;
可以稍微减轻痛苦。无论如何,由于没有它,我自己进行了尝试,这对方案没有任何影响。反正。最近,这些脚本之一具有用于创建表的语句以及用于向现有表添加检查约束的其他语句。我的一个用户运行了脚本,并在约束上遇到了错误;原来他有违反限制的现有数据。好的,没问题,请执行
ROLLBACK
。是的,不需要做一个,XACT_ABORT
已经做到了。结束并修复数据行…完成。现在重试! Uhp ...什么?这次在约束上没有错误,但是在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;
最佳答案
SQL Server具有不幸的错误处理行为。根据特定的错误,它有时会中止语句,批处理,事务和连接,或者中止或不执行任何操作。这是一个非常容易出错的模型。
在您的情况下,批处理和事务由于XACT_ABORT
而中止。但是GO
是批处理分隔符。您有多个批次。以后的批次继续运行。
使用一个批次,或使以后的批次检查先前的批次是否已运行(也许通过检查@@TRANCOUNT
)。
(SQL Server的一个更好的模型是回滚事务,但保持其打开状态,并使所有将来的语句失败。这将使其余脚本保留在事务中,并且不会泄漏任何内容。SQLServer不具有此功能)
关于sql-server - SQL Server中的CREATE TABLE语句不受ROLLBACK的影响吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25653047/