当使用THROW时,SQL Server会回滚我的事务

6

我在一个表上设置了一个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

如果运行回滚命令时,它会告诉你没有开始事务。
如果我将THROW替换成一个“普通”的异常(比如SELECT 1/0),事务不会被回滚。
我已经检查了xact_abort标志,它是关闭的。
使用SQL Server 2012并通过SSMS进行测试。
感谢任何帮助。
编辑 阅读@ Dan Guzman发布的文章后,我得出以下结论/总结......
SQL Server会在触发器中自动设置XACT_ABORT ON。
我的示例(上面)并未说明我的情况-实际上,我正在使用触发器创建扩展约束。
我的用例是人为的,我试图测试同一个单元测试中的多种情况(不是真实世界的情况,也不是良好的单元测试实践)。
我处理扩展约束检查并在触发器中抛出错误是正确的,但是在没有回滚事务的情况下没有真正的情况。
对于特定情况,在触发器中将XACT_ABORT OFF设置为有用;但是,您的交易仍将受到一般批次中止错误(例如死锁)的影响。
除了历史原因外,我不同意SQL Server的处理方式;仅仅因为当前没有你想继续交易的情况,并不意味着不会出现这样的情况。 我希望能够设置SQL Server以维护事务的完整性,如果您选择的架构是在源头严格管理事务,即“只有启动事务的人才能完成它”。这是在通常的故障保护措施之外的,例如,如果由于系统故障等原因未达到代码。
1个回答

4
THROW指在TRY/CATCH之外时终止批处理(https://msdn.microsoft.com/en-us/library/ee677615.aspx)。这意味着批处理没有进行任何进一步处理,包括插入语句后面的语句。您需要使用TRY/CATCH来包围您的INSERT或者使用RAISERROR代替THROW
T-SQL错误处理是一个相当复杂的主题。我建议您阅读Erland Sommarskog的一系列错误处理文章:http://www.sommarskog.se/error_handling/Part1.html。最相关的是主题Can I Prevent the Trigger from Rolling Back the Transaction?http://www.sommarskog.se/error_handling/Part3.html#Triggers。从最佳实践的角度来看,如果您在触发器中执行业务规则而没有回滚,则触发器不是正确的解决方案。

不错的文章-谢谢Dan。@usr,触发器必须在数据库级别上,它们是扩展约束的一部分。在具有多个客户端(包括SSIS)的系统中,您无法在任何地方强制执行检查。我的示例没有给出约束检查的全部细节。Dan和文章都没有说不要在触发器中抛出错误,他们说不要试图抑制回滚。 - Gilbert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接