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/CATCH
将 INSERT
括起来,或者使用 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。从最佳实践的角度来看,如果您在没有回滚的情况下在触发器中强制执行业务规则,那么触发器不是正确的解决方案。
我的一个表上有一个 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/CATCH
将 INSERT
括起来,或者使用 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。从最佳实践的角度来看,如果您在没有回滚的情况下在触发器中强制执行业务规则,那么触发器不是正确的解决方案。