【发布时间】:2020-02-21 12:15:57
【问题描述】:
问题
SET XACT_ABORT 的The documentation 仅说明启用此选项的效果。
当 SET XACT_ABORT 为 ON 时,如果 Transact-SQL 语句引发运行时错误,则整个事务将终止并回滚。
我不相信这是全部真相。读完后,我担心如果启用此选项的存储过程在由外部进程创建的事务中执行,它可能最终会回滚外部事务。幸运的是,我的担心被证明是没有根据的。但是,这意味着现在我并不真正了解XACT_ABORT 的工作原理。 SQL Server 检查事务是否应该回滚的条件是什么?
事先调查
我进行了以下实验:(下面是这段代码的摘要,因为在代码块破坏 StackOverflow 的格式之前有一个编号列表,duh)
CREATE TABLE Dummy
(
ID INT NOT NULL IDENTITY CONSTRAINT PK_Dummy PRIMARY KEY,
Text NVARCHAR(128) NOT NULL
)
CREATE UNIQUE NONCLUSTERED INDEX IX_Dummy_Text ON dbo.Dummy(Text)
GO
CREATE OR ALTER PROCEDURE InsertDummy
@Text NVARCHAR(128)
AS
BEGIN
SET NOCOUNT OFF
SET XACT_ABORT ON
INSERT dbo.Dummy (Text) VALUES (@Text)
END
GO
SET XACT_ABORT ON
BEGIN TRANSACTION
BEGIN TRY
EXEC dbo.InsertDummy @Text = N'Dummy'
EXEC dbo.InsertDummy @Text = N'Dummy' --DUPLICATE!
END TRY
BEGIN CATCH
PRINT 'ERROR! @@TRANCOUNT is ' + CONVERT(NVARCHAR, @@TRANCOUNT)
-- Echo the error
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT @ErrorMessage = ERROR_MESSAGE();
SELECT @ErrorSeverity = ERROR_SEVERITY();
SELECT @ErrorState = ERROR_STATE();
RAISERROR (@ErrorMessage, -- Message text.
@ErrorSeverity, -- Severity.
@ErrorState -- State.
);
END CATCH
PRINT 'At the end @@TRANCOUNT is ' + CONVERT(NVARCHAR, @@TRANCOUNT)
IF @@TRANCOUNT>0
ROLLBACK
- 创建具有唯一索引的 Dummy 表
- 插入 Dummy 的存储过程。该程序启用
XACT_ABORT。 - 在一个事务中执行此过程两次的代码。第二次调用失败,因为它试图将重复值插入 Dummy。
- 相同的代码打印出
@@TRANCOUNT值,以显示我们是否仍在交易中。它还启用了XACT_ABORT。
这个测试的输出是:
(1 row affected)
(0 rows affected)
ERROR! @@TRANCOUNT is 1
Msg 50000, Level 14, State 1, Line 74
Cannot insert duplicate key row in object 'dbo.Dummy' with unique index 'IX_Dummy_Text'. The duplicate key value is (Dummy).
At the end @@TRANCOUNT is 1
引发了错误,但事务未回滚。这种设置的工作方式显然不像文档让我相信的那么简单。为什么事务没有回滚?
This answer 提到XACT_ABORT 仅在错误严重程度至少为 16 时才回滚事务。此示例中的错误仅为 14 级。但是,即使我在过程中替换了INSERT使用RAISERROR (N'Custom error', 16, 0),事务仍然没有回滚。
更新:我发现,虽然在我的测试中事务没有回滚,但它注定了! @@TRANCOUNT 是1,当我执行此示例时,不管XACT_ABORT 设置如何:但如果设置为ON,XACT_STATE() 是-1,表示一个不可提交的事务。当XACT_ABORT 为OFF 时,XACT_STATE() 为1。
【问题讨论】:
-
仅供参考,您在这些语句中嵌套了事务,这是一个神话。
-
我推荐阅读all of this。 T-SQL 中的错误处理非常复杂,您不会从文档中获得完整的故事。
-
@Larnu 好吧,实际上我没有:这个示例中的过程只会创建一个保存点。抱歉,也许我应该通过删除此条件逻辑来简化示例,但我想坚持 SAVE TRANSACTION 文档中建议的模式以及我的实际生产代码过程使用的模式。
-
你在你的样本中做,@kamilk。您从
BEGIN TRANSACTION开始,然后继续使用EXEC dbo.InsertDummy。在dbo.InsertDummy中,您还有一个BEGIN TRANSACTION;。因此嵌套事务。 -
@Larnu 第二个
BEGIN TRANSACTION不会被执行,因为@TranCounter将等于1。
标签: sql sql-server stored-procedures transactions