当前位置: 首页 > 知识库问答 >
问题:

XACT _中止并不总是在出错时回滚事务。具体是什么时候呢?

南宫森
2023-03-14

< code>SET XACT_ABORT的文档对启用该选项的效果介绍不多。

当 SET XACT_ABORT 处于打开状态时,如果 Transact-SQL 语句引发运行时错误,则整个事务将终止并回滚。

我不相信这是全部事实。读完这篇文章后,我担心如果在由外部进程创建的事务中执行启用该选项的存储过程,它可能会回滚外部事务。幸运的是,我的担心被证明是没有根据的。然而,这意味着现在我并不真正理解< code > XACT _中止是如何工作的。SQL Server检查事务是否应该回滚的条件是什么?

我进行了以下实验:(下面是这段代码的摘要,因为在代码块破坏StackOverflow的格式之前有一个有序列表,嗯)

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
    < li >创建具有唯一索引的虚拟表 < li >插入到Dummy中的存储过程。该程序启用< code > XACT _中止。 < li >在一个事务中执行此过程两次的代码。第二个调用失败了,因为它试图向Dummy中插入一个重复的值。 < li >相同的代码打印出< code>@@TRANCOUNT值,以显示我们是否仍在事务中。它还启用了< code > XACT _中止。

此测试的输出为:

(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

引发了一个错误,但是事务没有回滚。这个设置的工作方式显然不像留档让我相信的那样简单。为什么事务没有回滚?

这个答案提到XACT_ABORT只有当错误的严重程度至少为16时才回滚事务。这个例子中的错误只有14级。但是,即使我用RAISERROR(N'Custom error',16,0)替换过程中的INSERT,事务仍然没有回滚。

更新:我发现,虽然在我的测试中没有回滚事务,但它是注定的@@TRANCOUNT是<code>1XACT_ABORT设置如何:但是如果设置是<code>ON,<code>XACT_ STATE()是<code>-1XACT_ABORT为OFFXACT_STATE()1

共有1个答案

百里胜泫
2023-03-14

问题“提出了一个错误,但事务没有回滚。这些设置的工作方式显然不像留档让我相信的那样简单。为什么事务没有回滚”

答案是RAISERROR不会导致XACT_ABORT触发!这意味着我们可能处于一个非常混乱的状态:事务中止,中止,我们是XACT_Abort:ing,还是我们?!

根据 MSDN ,

THROW语句支持SET XACT_ABORT。RAISERROR没有。新的应用程序应该使用THROW而不是RAISERROR。

我们可以使用 THROW 语句而不是引发器。因此,我们可以使用以下语句来触发XACT_ABORT

TRUNCATE TABLE Dummy
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
THROW
END CATCH

PRINT 'At the end @@TRANCOUNT is ' + CONVERT(NVARCHAR, @@TRANCOUNT)
IF @@TRANCOUNT>0
    ROLLBACK

输出将是;

(1 row affected)

(0 rows affected)
Msg 2601, Level 14, State 1, Procedure dbo.InsertDummy, Line 7 [Batch Start Line 5]
Cannot insert duplicate key row in object 'dbo.Dummy' with unique index 'IX_Dummy_Text'. The duplicate key value is (Dummy).

对于更新的问题,您可以看到set xact_abort on和try-catch在一起

 类似资料:
  • 考虑以下两行代码: 对我来说,他们看起来很相似。但是,第二行被Java编译器(1.8)拒绝,消息是“不能推断SimpleFileVisitor的类型参数<>”。 谁能解释一下,有什么问题吗?

  • 问题内容: 我正在编写一些JavaScript代码来解析用户输入的功能(用于类似于电子表格的功能)。解析了公式之后,我 可以 将其转换为JavaScript并在其上运行以产生结果。 但是,如果能避免,我总是避免使用它,因为它是evil的(而且,无论对与错,我一直认为它在JavaScript中更为evil,因为要评估的代码可能会被用户更改)。 那么,何时可以使用它呢? 问题答案: 我想花点时间解决您

  • 问题内容: 在我用php开发的所有年份中,我一直都听说使用是邪恶的。 考虑以下代码,使用第二个(更优雅)的选项是否有意义?如果没有,为什么? 问题答案: 在将eval()称为纯邪恶时,我会保持谨慎。 动态评估是一个强大的工具,有时可以节省生命。使用eval()可以解决PHP的缺点(请参见下文)。 eval()的主要问题是: 潜在的不安全输入。 传递不受信任的参数是一种失败的方法。确保参数(或其一部

  • 我们为JPA配置了Spring数据。服务事务方法不会因错误而回滚(例如,DB ConstraintViolationException)。 我能找到的最接近的是这个(事务不回滚)Spring-data、JTA、JPA、Wildfly10但是我们没有任何XML配置,我们所有的配置都是基于Java的。 本质上,服务方法如下所示:不捕获错误,抛出所有内容。

  • 本文向大家介绍什么是事务?什么是锁?相关面试题,主要包含被问及什么是事务?什么是锁?时的应答技巧和注意事项,需要的朋友参考一下 答:事务就是被绑定在一起作为一个逻辑工作单元的SQL语句分组,如果任何一个语句操作失败那么整个操作就被失败,以后操作就会回滚到操作前状态,或者是上有个节点。为了确保要么执行,要么不执行,就可以使用事务。要将有组语句作为事务考虑,就需要通过ACID测试,即原子性,一致性,隔

  • 你能告诉我如何只对某些代码块运行事务吗?我有@Transactional的服务函数,并从它们调用存储库函数,在那里执行查询。但当查询失败时,由于事务必须结束,服务功能也随之失效。那么,当存储库函数失败时,如何返回null值呢?我需要在失败时捕获空值,并继续一些逻辑。我的代码: 我尝试了@davidxxx的答案,得到了这个错误: