当XACT_ABORT设置为ON时,在什么情况下可以从CATCH块内部提交事务?


13

我一直在阅读有关TRY...CATCH和的MSDN XACT_STATE

它具有以下示例,该示例XACT_STATE在构造CATCH块中TRY…CATCH用于确定是提交还是回退事务:

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;
        -- A FOREIGN KEY constraint exists on this table. This 
        -- statement will generate a constraint violation error.
        DELETE FROM Production.Product
            WHERE ProductID = 980;

    -- If the delete operation succeeds, commit the transaction. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Test XACT_STATE for 0, 1, or -1.
    -- If 1, the transaction is committable.
    -- If -1, the transaction is uncommittable and should 
    --     be rolled back.
    -- XACT_STATE = 0 means there is no transaction and
    --     a commit or rollback operation would generate an error.

    -- Test whether the transaction is uncommittable.
    IF (XACT_STATE()) = -1
    BEGIN
        PRINT 'The transaction is in an uncommittable state.' +
              ' Rolling back transaction.'
        ROLLBACK TRANSACTION;
    END;

    -- Test whether the transaction is active and valid.
    IF (XACT_STATE()) = 1
    BEGIN
        PRINT 'The transaction is committable.' + 
              ' Committing transaction.'
        COMMIT TRANSACTION;   
    END;
END CATCH;
GO

我不明白的是,为什么我应该关心并检查XACT_STATE退货情况?

请注意,示例中将标志XACT_ABORT设置为ON

如果TRY块内有足够严重的错误,则控件将传递到CATCH。因此,如果我在内CATCH,我知道交易有问题,在这种情况下,真正唯一明智的做法是将其回滚,不是吗?

但是,来自MSDN的此示例表明,在某些情况下,当控制权传递到CATCH该事务时,提交事务仍然有意义。有人可以提供一些实际的例子,说明何时可能发生,何时有意义?

我没有在什么情况下,控制里面可以看到通过CATCH与可提交的事务时,XACT_ABORT被设置为ON

关于MSDN的文章SET XACT_ABORT提供了一个示例,当将事务中的某些语句成功执行而将某些语句XACT_ABORT设置为时,则失败OFF,我理解这一点。但是,SET XACT_ABORT ON如何XACT_STATE()CATCH块内返回1 ?

最初,我会这样编写代码:

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;
        -- A FOREIGN KEY constraint exists on this table. This 
        -- statement will generate a constraint violation error.
        DELETE FROM Production.Product
            WHERE ProductID = 980;

    -- If the delete operation succeeds, commit the transaction. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Some severe problem with the transaction
    PRINT 'Rolling back transaction.';
    ROLLBACK TRANSACTION;
END CATCH;
GO

考虑到Max Vernon的回答,我将这样编写代码。他表明,在尝试之前检查是否有活跃的交易是有意义的ROLLBACK。尽管如此,使用SET XACT_ABORT ONCATCH块可以有注定的交易或根本没有交易。因此,无论如何都没有什么可做的COMMIT。我错了吗?

USE AdventureWorks2012;
GO

-- SET XACT_ABORT ON will render the transaction uncommittable
-- when the constraint violation occurs.
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;
        -- A FOREIGN KEY constraint exists on this table. This 
        -- statement will generate a constraint violation error.
        DELETE FROM Production.Product
            WHERE ProductID = 980;

    -- If the delete operation succeeds, commit the transaction. The CATCH
    -- block will not execute.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Some severe problem with the transaction
    IF (XACT_STATE()) <> 0
    BEGIN
        -- There is still an active transaction that should be rolled back
        PRINT 'Rolling back transaction.';
        ROLLBACK TRANSACTION;
    END;

END CATCH;
GO

Answers:


8

事实证明,如果设置为,则不能CATCH块内部提交事务。XACT_ABORTON

MSDN中的示例在某种程度上具有误导性,因为检查表明XACT_STATE在某些情况下可以返回1,并且可能进行COMMIT事务处理。

IF (XACT_STATE()) = 1
BEGIN
    PRINT 'The transaction is committable.' + 
          ' Committing transaction.'
    COMMIT TRANSACTION;   
END;

并非如此,如果设置为,XACT_STATE则永远不会在CATCHblock 内部返回1 。XACT_ABORTON

似乎MSDN示例代码旨在主要说明XACT_STATE()功能的使用,而与XACT_ABORT设置无关。该示例代码看起来足够通用,可以同时XACT_ABORT设置为ONOFF。只是有了XACT_ABORT = ON检查IF (XACT_STATE()) = 1就变得不必要了。


Erland Sommarskog提供了一系列非常详细的有关SQL Server中错误和事务处理的文章。在第2部分-错误分类中,他提供了一个综合表,该表汇总了所有错误类别以及SQL Server如何处理它们以及如何TRY ... CATCH以及如何XACT_ABORT更改行为。

+-----------------------------+---------------------------++------------------------------+
|                             |     Without TRY-CATCH     ||        With TRY-CATCH        |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
|              SET XACT_ABORT |  OFF  |  ON   | OFF | ON  ||    ON or OFF     | OFF | ON  |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
| Class Name                  |    Aborts     |   Rolls   ||    Catchable     |   Dooms   |
|                             |               |   Back    ||                  |transaction|
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
| Fatal errors                |  Connection   |    Yes    ||       No         |    n/a    |
| Batch-aborting              |     Batch     |    Yes    ||       Yes        |    Yes    |
| Batch-only aborting         |     Batch     | No  | Yes ||       Yes        | No  | Yes |
| Statement-terminating       | Stmnt | Batch | No  | Yes ||       Yes        | No  | Yes |
| Terminates nothing at all   |    Nothing    |    No     ||       Yes        | No  | Yes |
| Compilation: syntax errors  |  (Statement)  |    No     ||       Yes        | No  | Yes |
| Compilation: binding errors | Scope | Batch | No  | Yes || Outer scope only | No  | Yes |
| Compilation: optimisation   |     Batch     |    Yes    || Outer scope only |    Yes    |
| Attention signal            |     Batch     | No  | Yes ||       No         |    n/a    |
| Informational/warning msgs  |    Nothing    |    No     ||       No         |    n/a    |
| Uncatchable errors          |    Varying    |  Varying  ||       No         |    n/a    |
+-----------------------------+-------+-------+-----+-----++------------------+-----+-----+

表中的最后一列回答了该问题。随着TRY-CATCHXACT_ABORT ON该交易在所有可能的情况是注定的。

问题范围之外的一则注释。正如Erland所说,这种一致性是设置XACT_ABORTON

我已经建议您的存储过程应包含命令SET XACT_ABORT, NOCOUNT ON。查看上面的表格,您会发现实际上具有XACT_ABORT更高的一致性。例如,交易总是注定要失败。在下面的内容中,我将显示设置XACT_ABORT为的许多示例OFF,以便您了解为什么应避免使用此默认设置。


7

我会采取不同的方式。XACT_ABORT_ON是一把大锤,您可以使用更精细的方法,请参阅异常处理和嵌套事务

create procedure [usp_my_procedure_name]
as
begin
    set nocount on;
    declare @trancount int;
    set @trancount = @@trancount;
    begin try
        if @trancount = 0
            begin transaction
        else
            save transaction usp_my_procedure_name;

        -- Do the actual work here

lbexit:
        if @trancount = 0   
            commit;
    end try
    begin catch
        declare @error int, @message varchar(4000), @xstate int;
        select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
        if @xstate = -1
            rollback;
        if @xstate = 1 and @trancount = 0
            rollback
        if @xstate = 1 and @trancount > 0
            rollback transaction usp_my_procedure_name;

        raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
    end catch   
end
go

如果可能,此方法将仅回滚TRY块内部执行的工作,并将状态恢复到进入TRY块之前的状态。这样,您可以执行复杂的处理,例如迭代游标,在发生错误时不会丢失所有工作。唯一的缺点是,通过使用事务保存点,您将不能使用任何与保存点不兼容的东西,例如分布式事务。


感谢您的答复,但问题不是我们应该设置XACT_ABORTON还是OFF
弗拉基米尔·巴拉诺夫(Fladimir Baranov)

7

TL; DR /执行摘要:关于问题的这一部分:

我没有在什么情况下,控制里面可以看到通过CATCH与可提交的事务时,XACT_ABORT被设置为ON

我现在已经对此进行了大量测试,并且找不到当when session属性为is 时在块内XACT_STATE()返回的任何情况。实际上,根据SET XACT_ABORT的当前MSDN页面:1CATCH@@TRANCOUNT > 0 XACT_ABORTON

当SET XACT_ABORT为ON时,如果Transact-SQL语句引发运行时错误,则整个事务将终止并回滚。

该声明似乎与您的推测和我的发现一致。

关于MSDN的文章SET XACT_ABORT提供了一个示例,其中将事务中的某些语句成功执行而将某些语句XACT_ABORT设置为则失败OFF

没错,在该例子语句是不是一个内TRY块。TRY块中的那些相同语句仍会阻止执行引起错误的语句之后的任何语句,但是假设XACT_ABORTOFF,当控制权传递给该CATCH块时,Transaction在物理上仍然有效,因为所有先前更改均已发生而没有错误并且可以被提交,如果是这样的愿望,或者他们可以回滚。另一方面,如果XACT_ABORT是,ON则任何先前的更改都会自动回滚,然后您可以选择以下一种:a)发出aROLLBACK这大部分只是对情况的接受,因为交易已经回滚,减去了重置@@TRANCOUNT0,或b)收到错误。没有太多选择,是吗?

对于这个难题,一个可能不重要的细节(在该文档中找不到)是SET XACT_ABORTSQL Server 2000以来,此会话属性甚至该示例代码就已经存在(两个版本之间的文档几乎相同),早于该TRY...CATCH结构。在SQL Server 2005中引入再次是看文档和例子看(没有TRY...CATCH),使用XACT_ABORT ON原因进行即时交易的回滚:有“不可提交”(请注意,无交易状态,没有提及在该SET XACT_ABORT文档中所有“不可提交的”交易状态)。

我认为得出以下结论是合理的:

  1. TRY...CATCHSQL Server 2005 中该结构的引入产生了对新事务状态(即“不可提交”)的需求以及XACT_STATE()获取该信息的功能。
  2. 如果同时满足以下两个条件,则仅XACT_STATE()CATCH块中检查才有意义:
    1. XACT_ABORTOFF(否则XACT_STATE()应该总是返回-1并且@@TRANCOUNT将是您所需要的)
    2. 您可以在代码CATCH块中,或者在调用嵌套的情况下,在代码链中某个位置进行逻辑处理,而不是执行操作即可进行更改(a COMMIT或什至任何DML,DDL等语句)。(这是一个非常不典型的用例)**请参阅UPDATE 3部分底部的注释,该注释关于Microsoft始终建议而不是进行检查的非官方建议,以及为什么测试表明他们的推理没有成功。ROLLBACKXACT_STATE()@@TRANCOUNT
  3. TRY...CATCH在SQL Server 2005中,该构造的引入在大多数情况下已废除了XACT_ABORT ONsession属性,因为它提供了对Transaction的更大程度的控制(您至少可以选择COMMIT,前提是XACT_STATE()不返回-1)。
    另一种方式来看待,这是,之前到SQL Server 2005XACT_ABORT ON提供了一个方便,可靠的方式来停止处理时发生错误,较检查@@ERROR每个语句后。
  4. 为文档例如代码XACT_STATE()是错误的,或充其量误导,因为它显示了检查XACT_STATE() = 1XACT_ABORTON

较长的部分;-)

是的,MSDN上的示例代码有点令人困惑(另请参阅:@@ TRANCOUNT(回滚)与XACT_STATE);-)。而且,我觉得这是误导,因为它要么显示的东西,是没有意义的(对于您所问的原因:你能即使在具有“可提交”交易CATCH时,块XACT_ABORTON),甚至如果可能的话,它仍然专注于很少有人会想要或不需要的技术可能性,而忽略了人们更可能需要它的原因。

如果TRY块内有足够严重的错误,则控件将传递到CATCH中。因此,如果我在CATCH内,我知道交易有问题,实际上在这种情况下唯一明智的做法是将其回滚,不是吗?

我认为,如果我们确保某些单词和概念的含义在同一页上,将会有所帮助:

  • “严重程度足够高的错误”:明确起见TRY ... CATCH将捕获大多数错误。在该链接的MSDN页面上的“不受TRY…CATCH构造影响的错误”部分下,列出了不会捕获的内容。

  • “如果我在CATCH内,我知道事务有问题”(添加了pha):如果通过“事务”是指由您将语句分组为显式事务确定的逻辑工作单元,则很可能是。我认为我们大多数数据库专家都会同意回滚是“唯一明智的做法”,因为我们可能对如何以及为何使用显式事务有相似的看法,并设想应该组成原子单元的步骤工作的。

    但是,如果您的意思是将实际的工作单元分组到显式事务中,那么不,您不知道事务本身存在问题。您只知道在显式定义的事务中执行语句引发了错误。但这可能不是DML或DDL语句。即使是DML语句,事务本身也可能是可提交的。

鉴于以上两点,我们可能应该在您“不能”提交的事务和您“不想”提交的事务之间进行区分。

XACT_STATE()返回1时,表示事务是“可提交的”,您可以在COMMIT或之间进行选择ROLLBACK。您可能不想提交它,但是如果出于某些原因甚至很难举一个例子,您至少可以这样做,因为事务的某些部分确实成功完成了。

但是,当XACT_STATE()返回a时-1,您确实需要这样做,ROLLBACK因为Transaction的某些部分进入了不良状态。现在,我确实同意,如果控制权已经传递给CATCH块,那么只需进行检查就足够了@@TRANCOUNT,因为即使您可以提交事务,为什么还要这么做?

但是,如果您在示例的顶部注意到,则XACT_ABORT ON更改设置会有所改变。您可能会遇到一个常规错误,执行BEGIN TRAN此操作后,将在XACT_ABORTis OFF结束时将控制权传递给CATCH块,并且XACT_STATE()将返回1。但是,如果XACT_ABORT为ON,则由于任何'ol错误,事务将“中止”(即无效),然后XACT_STATE()将返回-1。在这种情况下,似乎没用检查XACT_STATE()的内CATCH块,因为它似乎总是返回-1XACT_ABORTON

那么那是XACT_STATE()为了什么呢?一些线索是:

  • TRY...CATCH在“不可提交的事务和XACT_STATE”部分下的MSDN页面上,该页面显示:

    通常,在TRY块外部结束事务的错误会导致事务在TRY块内部发生错误时进入不可提交状态。

  • SET XACT_ABORT的MSDN页面在“备注”部分下显示:

    当SET XACT_ABORT为OFF时,在某些情况下,仅回滚引发错误的Transact-SQL语句,并且事务将继续处理。

    和:

    必须针对大多数OLE DB提供程序(包括SQL Server)在隐式或显式事务中为数据修改语句将XACT_ABORT设置为ON。

  • BEGIN TRANSACTION的MSDN页面的“备注”部分下显示:

    如果在提交或回滚该语句之前执行了以下操作,则由BEGIN TRANSACTION语句启动的本地事务将升级为分布式事务:

    • 执行引用链接服务器上的远程表的INSERT,DELETE或UPDATE语句。如果用于访问链接服务器的OLE DB提供程序不支持ITransactionJoin接口,则INSERT,UPDATE或DELETE语句将失败。

最适用的用法似乎是在Linked Server DML语句的上下文内。而且我相信自己几年前就遇到了这个问题。我不记得所有的细节,但是这与远程服务器不可用有关,并且由于某种原因,该错误没有被捕获到TRY块中,也从未发送给CATCH,所以确实不应该的COMMIT。当然,这可能是一个问题,就是没有XACT_ABORT设置为ON而不是不进行检查XACT_STATE(),或者可能没有两者。我确实记得读过一些内容,说如果您使用链接服务器和/或分布式事务,那么您需要使用XACT_ABORT ON和/或XACT_STATE(),但是我现在似乎找不到该文档。如果找到它,我将使用链接对其进行更新。

尽管如此,我还是尝试了几件事,但是无法找到具有报告功能XACT_ABORT ON并将控制权传递给报告CATCH块的方案。XACT_STATE()1

尝试这些示例,以查看XACT_ABORT对的值的影响XACT_STATE()

SET XACT_ABORT OFF;

BEGIN TRY
    BEGIN TRAN;

    SELECT 1/0 AS [DivideByZero]; -- error, yo!

    COMMIT TRAN;
END TRY
BEGIN CATCH
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            XACT_STATE() AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage]

    IF (@@TRANCOUNT > 0)
    BEGIN
        ROLLBACK;
    END;
END CATCH;

GO ------------------------------------------------

SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRAN;

    SELECT 1/0 AS [DivideByZero]; -- error, yo!

    COMMIT TRAN;
END TRY
BEGIN CATCH
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            XACT_STATE() AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage]

    IF (@@TRANCOUNT > 0)
    BEGIN
        ROLLBACK;
    END;
END CATCH;

GO ------------------------------------------------

SET XACT_ABORT ON;

BEGIN TRY
    SELECT 1/0 AS [DivideByZero]; -- error, yo!
END TRY
BEGIN CATCH
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            XACT_STATE() AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage]
END CATCH;

更新

虽然不是原始问题的一部分,但基于此答案的以下评论:

我一直在阅读Erland关于错误和事务处理的文章,他说这XACT_ABORTOFF出于遗留原因的默认设置,通常我们应该将其设置为ON
...
“ ...如果您遵循建议并在SET XACT_ABORT ON上运行,则该事务将永远失败。

XACT_ABORT ON到处使用之前,我会问:这里到底获得了什么?我认为没有必要这样做,并且通常主张您仅在必要时使用它。ROLLBACK通过使用@Remus的答案中显示的模板,或者我多年来使用的模板,基本上都一样,但是没有保存点,您是否想要足够容易地处理,如本答案所示(处理嵌套的调用):

我们是否需要使用C#代码以及存储过程来处理事务


更新2

我做了一些测试,这次是创建一个小的.NET Console应用程序,在执行任何SqlCommand对象之前(即通过using (SqlTransaction _Tran = _Connection.BeginTransaction()) { ...)在应用程序层中创建一个Transaction ,以及使用批中止错误而不是仅仅执行一条语句错误中止,发现:

  1. 大多数情况下,“不可提交的”事务已经回滚(更改已撤消),但@@TRANCOUNT仍大于0。
  2. 当您有“不可提交的”交易时,您将无法发出,COMMIT因为这将生成错误消息,指出该交易是“ 不可提交的”。您也不能忽略它/不执行任何操作,因为当该批处理结束时,将指出该批处理完成了无法解决的事务,并且会回滚(因此,嗯,如果它仍会自动回滚,为什么要麻烦抛出错误?)。因此,您必须在批处理结束之前发出一个explicit ROLLBACK,也许不是在立即CATCH块中。
  3. TRY...CATCH结构中,当XACT_ABORT为is时OFF,如果错误发生在TRY块外,则将自动终止事务的错误(例如,中止错误)将撤消工作,但不会终止Tranasction,将其保留为“不可提交”。签发a ROLLBACK只是完成交易所需的手续,但工作已被回滚。
  4. XACT_ABORTON,大多数错误作为分批中止,并作为直接在上面(#3)在项目符号点描述因此表现。
  5. XACT_STATE(),至少在一个CATCH块中,-1如果在错误发生时有活动的交易,则将显示批中止错误。
  6. XACT_STATE()有时1即使没有活动的交易也会返回。如果@@SPID(以及其他)在SELECT列表中XACT_STATE(),则XACT_STATE()在没有活动的Transaction时将返回1。此行为始于SQL Server 2012,并于2014年存在,但我尚未在2016年进行测试。

考虑到以上几点:

  • 鉴于分#4,#5,因为大部分(或全部?)错误将呈现交易“uncommitable”,似乎完全没有意义的检查XACT_STATE()CATCH块的时候XACT_ABORTON因为返回的永远是值-1
  • 检查XACT_STATE()CATCH块时XACT_ABORTOFF更有意义,因为返回值将至少有一些变化,因为它会返回1的语句中止错误。但是,如果您像我们大多数人一样进行编码,则这种区别是没有意义的,因为ROLLBACK无论如何,您都将仅出于发生错误的事实进行调用。
  • 如果发现确实令发出的情况COMMITCATCH块,然后检查的价值XACT_STATE(),并确保SET XACT_ABORT OFF;
  • XACT_ABORT ON似乎并没有提供任何好处TRY...CATCH
  • 我发现没有一种情况XACT_STATE()比单纯的检查能够提供有意义的好处@@TRANCOUNT
  • 在那里我还可以找到任何情况下XACT_STATE()返回1CATCH块时XACT_ABORTON。我认为这是文档错误。
  • 是的,您可以回滚未明确开始的事务。在使用的情况下XACT_ABORT ON,这是有争议的,因为在TRY块中发生的错误将自动回滚更改。
  • TRY...CATCH构造的好处XACT_ABORT ON是不会自动取消整个Transaction,因此允许提交Transaction(只要XACT_STATE()returns 1)(即使这是一个极端情况)。

XACT_STATE()返回-1when的示例XACT_ABORTOFF

SET XACT_ABORT OFF;

BEGIN TRY
    BEGIN TRAN;

    SELECT CONVERT(INT, 'g') AS [ConversionError];

    COMMIT TRAN;
END TRY
BEGIN CATCH
    DECLARE @State INT;
    SET @State = XACT_STATE();
    SELECT @@TRANCOUNT AS [@@TRANCOUNT],
            @State AS [XactState],
            ERROR_MESSAGE() AS [ErrorMessage];

    IF (@@TRANCOUNT > 0)
    BEGIN
        SELECT 'Rollin back...' AS [Transaction];
        ROLLBACK;
    END;
END CATCH;

更新3

与UPDATE 2部分中的项目#6相关(即,XACT_STATE()当没有活动Transaction时可能返回的错误值):

  • 奇怪/错误的行为始于SQL Server 2012(到目前为止已针对2012 SP2和2014 SP1进行了测试)
  • 在SQL Server 2005、2008和2008 R2中,XACT_STATE()在触发器或INSERT...EXEC场景中使用时未报告预期值:xact_state()无法可靠地用于确定事务是否注定失败。但是,(我只在2008 R2测试)这3个版本,XACT_STATE()没有错误地报告1在使用时SELECT@@SPID
  • 有一个针对此处提到的行为的Connect错误,但已通过“按设计”关闭:XACT_STATE()可以在SQL 2012中返回错误的事务状态。但是,在从DMV中进行选择时进行了测试,并得出结论,这样做自然会产生系统生成的事务,至少对于某些DMV而言。MS在最终答复中还指出:

    请注意,IF语句以及没有FROM的SELECT都不会启动事务。
    例如,如果您没有以前存在的事务,则运行SELECT XACT_STATE()将返回0。

    在以下示例中,这些语句是错误的:

    SELECT @@TRANCOUNT AS [TRANCOUNT], XACT_STATE() AS [XACT_STATE], @@SPID AS [SPID];
    GO
    DECLARE @SPID INT;
    SET @SPID = @@SPID;
    SELECT @@TRANCOUNT AS [TRANCOUNT], XACT_STATE() AS [XACT_STATE], @SPID AS [SPID];
    GO
  • 因此,新的Connect错误:
    当在SELECT中使用某些系统变量但不使用FROM子句时,XACT_STATE()返回1

请注意,在上面直接链接的“ XACT_STATE()可以在SQL 2012中返回错误的事务状态”连接项中,Microsoft(代表)表示:

@@ trancount返回BEGIN TRAN语句的数量。因此,它不是是否存在活动交易的可靠指标。如果存在活动的自动提交事务,则XACT_STATE()也会返回1,因此它是是否存在活动事务的更可靠的指示符。

但是,我找不到不信任的理由@@TRANCOUNT。以下测试表明@@TRANCOUNT确实确实1在自动提交事务中返回:

--- begin setup
GO
CREATE PROCEDURE #TransactionInfo AS
SET NOCOUNT ON;
SELECT @@TRANCOUNT AS [TranCount],
       XACT_STATE() AS [XactState];
GO
--- end setup

DECLARE @Test TABLE (TranCount INT, XactState INT);

SELECT * FROM @Test; -- no rows

EXEC #TransactionInfo; -- 0 for both fields

INSERT INTO @Test (TranCount, XactState)
    EXEC #TransactionInfo;

SELECT * FROM @Test; -- 1 row; 1 for both fields

我还使用触发器@@TRANCOUNT在真实表上进行了测试,1即使没有启动显式事务,触发器内的报告也能准确报告。


4

防御性编程要求您编写处理尽可能多的已知状态的代码,从而减少发生错误的可能性。

检查XACT_STATE()以确定是否可以执行回滚只是一个好习惯。盲目尝试回滚意味着您可能会无意中引起TRY ... CATCH内部错误。

如果您未明确启动事务,则回滚可能在TRY ... CATCH内部失败。复制和粘贴代码块可能很容易导致这种情况。


感谢您的回复。我只是想不到一个简单的例子ROLLBACK无法在内部运行的情况,CATCH而您给出了一个很好的例子。我猜想,如果TRY ... CATCH ... ROLLBACK涉及嵌套事务和嵌套存储过程及其自身,也会很快变得混乱。
弗拉基米尔·巴拉诺夫(Fladimir Baranov)

不过,如果您能扩大对第二部分的回答,我将不胜感激- IF (XACT_STATE()) = 1 COMMIT TRANSACTION; 我们如何才能CATCH通过可提交事务最终解决问题?我不敢从中提交一些(可能)垃圾CATCH。我的理由是:如果我们内部发生了CATCH某些错误,那么我将无法相信数据库的状态,因此,ROLLBACK无论如何我们都最好。
弗拉基米尔·巴拉诺夫(Fladimir Baranov)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.