回滚 DDL 语句组

Der*_*omm 5 sql-server sql-server-2008-r2 ddl rollback

在 SQL Server 2008 R2 中工作时,我试图将一组 DDL 语句作为一个组回滚(想想数据库的升级脚本),但遇到了麻烦。

取以下代码:

begin try

begin tran

create table foo (i int)
alter table foo add x dog
insert into foo select 1,1
insert into foo select 1,1,1

commit tran

end try

begin catch

rollback tran
print @@error

end catch
Run Code Online (Sandbox Code Playgroud)

我期待尝试在更改表语句上失败,下降到捕获,回滚事务,并打印错误消息。但是,如果您检查对象/表,您会看到 foo 仍然存在(因此创建表没有正确回滚)。

select * from sys.objects where name = 'foo'
Run Code Online (Sandbox Code Playgroud)

我在这里做错了什么?

cfr*_*urg 5

发生错误是因为由于延迟名称解析而引发的错误是重新编译错误的一部分。查看 SQL BOL,当它们发生在与 try...catch 相同的级别时,它们不会被困住。但是,如果它发生在不同的级别,无论是动态 SQL 还是 SP 调用,它都会被捕获并回滚。

使用 Profiler,您可以看到“alter table foo add x dog”语句在执行之前重新编译,然后出错并绕过 catch 块。

有错误的探查器跟踪

如果您将语句包装在动态 SQL 中,则错误不会返回到 Profiler 并且事务将回滚

没有错误的探查器跟踪


Kev*_*sel 4

您看到此结果的原因是 SQL Server 实际上并未捕获您的 ALTER TABLE 错误。您会注意到,当您运行此命令时,您会看到红色错误消息而不是打印行 - 您可以通过更改print @@error为类似print 'HELLO!'; 来验证这一点。在这种情况下,您将不会看到“HELLO!” 打印; 您将会看到错误。在线书籍列出了您无法发现的错误情况

这里的另一种选择是在SET XACT_ABORT ON开始交易之前。然后,您可以在收到第一个错误时回滚更改。

  • 事实上,我在你的 BOL 链接中找到了一个伪答案。如果我创建一个简单的“exec sp_executesql @param_stmt”过程,并将我的 DDL 语句传递到该过程中,我可以将它们包装在 TRY/TRAN 中,并且似乎可以正确处理 COMMIT/ROLLBACK/CATCH。非常奇怪的行为,但我想 proc 的失败是可以捕获的,而单独的 DDL 语句则不能。感谢您将我指向正确的地方:) (2认同)
  • @Derek Red Gate SQL Compare 通过设置 XACT_ABORT ON、创建 #temp 表并在其中存储任何错误(在每个语句后检查)来实现此目的。[您可以在此处查看示例](http://social.msdn.microsoft.com/Forums/en-US/transactsql/thread/603afc2e-d65a-4960-a89e-11e31ab9730a)。这很痛苦,但根据您生成 DDL 语句的方式,您应该能够自动执行类似的操作。 (2认同)