SQL 服务器在使用 THROW 时回滚我的事务

SQL Server Rolls back my transaction when using THROW

我的一个表上有一个 INSERT 触发器,它在发现重复项时发出 THROW。问题是此时我的事务似乎被隐式回滚 - 这是一个问题,我想控制事务何时回滚。

可以使用此脚本重新创建问题:

CREATE TABLE xTable (
        id int identity not null
)
go
create trigger xTrigger on xTable after insert as
print 'inserting...';
throw 1600000, 'blah', 1
go

begin tran
insert into xTable default values
rollback tran

go
drop table xTable

如果您 运行 回滚 tran - 它会告诉您没有 begin tran。

如果我将 THROW 换成 'normal' 异常(如 SELECT 1/0),事务不会回滚。

我检查了 xact_abort 标志 - 它已关闭。

使用 SQL Server 2012 并通过 SSMS 进行测试

感谢任何帮助,谢谢。

编辑 在阅读了@Dan Guzman 发表的文章后,我得出以下结论 conclusion/summary...

SQL 服务器自动在触发器中设置 XACT_ABORT ON。

我的示例(以上)并未说明我的情况 - 实际上我正在使用触发器创建扩展约束。

我的用例是人为设计的,我试图在同一个单元测试中测试多种情况(不是真实世界的情况,也不是好的单元测试实践)。

我对扩展约束检查和在触发器中抛出错误的处理是正确的,但是没有我不想回滚事务的实际情况。

对于特定情况,在触发器内部设置 XACT_ABORT OFF 可能很有用;但您的交易仍会受到一般批处理中止错误(如死锁)的破坏。

抛开历史原因,我不同意SQL服务器的处理方式;仅仅因为当前没有您希望继续交易的情况,并不意味着这种情况不会出现。 我希望看到一个能够设置 SQL 服务器来维护事务的完整性,如果您选择的体系结构是要在源头严格管理事务,即 "he alone who starts the transaction, must finish it"。这个,除了通常的故障保险,例如如果由于系统故障等原因无法访问您的代码

THROW 将在超出 TRY/CATCH (https://msdn.microsoft.com/en-us/library/ee677615.aspx) 范围时终止批处理。这里的含义是不会对批处理进行进一步处理,包括插入之后的语句。您需要用 TRY/CATCHINSERT 括起来,或者使用 RAISERROR 而不是 THROW

T-SQL 错误处理是一个相当庞大和复杂的话题。我建议您仔细阅读 Erland Sommarskog 的错误处理系列文章:http://www.sommarskog.se/error_handling/Part1.html. Most relevant here is the topic Can I Prevent the Trigger from Rolling Back the Transaction? http://www.sommarskog.se/error_handling/Part3.html#Triggers。从最佳实践的角度来看,如果您在没有回滚的情况下在触发器中强制执行业务规则,那么触发器不是正确的解决方案。