AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 128535
Accepted
Vladimir Baranov
Vladimir Baranov
Asked: 2016-02-08 16:51:31 +0800 CST2016-02-08 16:51:31 +0800 CST 2016-02-08 16:51:31 +0800 CST

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

  • 772

我一直在阅读 MSDNTRY...CATCH和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_ABORTON

MSDN文章SET XACT_ABORT有一个示例,当事务中的某些语句成功执行而某些语句XACT_ABORT设置为时失败OFF,我理解。但是,SET XACT_ABORT ON如何在块XACT_STATE()内返回 1呢?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
    -- Some severe problem with the transaction
    PRINT 'Rolling back transaction.';
    ROLLBACK TRANSACTION;
END CATCH;
GO

考虑到 Max Vernon 的回答,我会这样编写代码。他表明,在尝试之前检查是否存在活动事务是有意义的ROLLBACK。尽管如此,区块可以有注定SET XACT_ABORT ON的交易或根本没有交易。CATCH所以,无论如何,没有什么可做的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
sql-server sql-server-2008
  • 4 4 个回答
  • 9528 Views

4 个回答

  • Voted
  1. Best Answer
    Vladimir Baranov
    2016-02-11T19:18:08+08:002016-02-11T19:18:08+08:00

    It turns out that transaction can not be committed from inside the CATCH block if XACT_ABORT is set to ON.

    The example from MSDN is somewhat misleading, because the check implies that XACT_STATE can return 1 in some cases and it may be possible to COMMIT the transaction.

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

    It is not true, XACT_STATE will never return 1 inside CATCH block if XACT_ABORT is set to ON.

    It seems that the MSDN sample code was meant to primarily illustrate the use of XACT_STATE() function regardless of the XACT_ABORT setting. The sample code looks generic enough to work with both XACT_ABORT set to ON and OFF. It is just that with XACT_ABORT = ON the check IF (XACT_STATE()) = 1 becomes unnecessary.


    There is a very good detailed set of articles about Error and Transaction Handling in SQL Server by Erland Sommarskog. In Part 2 - Classification of Errors he presents a comprehensive table that puts together all classes of errors and how they are handled by SQL Server and how TRY ... CATCH and XACT_ABORT changes the behaviour.

    +-----------------------------+---------------------------++------------------------------+
    |                             |     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    |
    +-----------------------------+-------+-------+-----+-----++------------------+-----+-----+
    

    The last column in the table answers the question. With TRY-CATCH and with XACT_ABORT ON the transaction is doomed in all possible cases.

    One note outside the scope of the question. As Erland says, this consistency is one of the reasons to set XACT_ABORT to ON:

    I have already given the recommendation that your stored procedures should include the command SET XACT_ABORT, NOCOUNT ON. If you look at the table above, you see that with XACT_ABORT in effect, there is some higher level of consistency. For instance, the transaction is always doomed. In the following, I will show many examples where I set XACT_ABORT to OFF, so that you can get an understanding of why you should avoid this default setting.

    • 11
  2. Solomon Rutzky
    2016-02-08T18:53:00+08:002016-02-08T18:53:00+08:00

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

    我看不到在什么情况下可以通过CATCH设置为.XACT_ABORTON

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

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

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

    MSDN文章SET XACT_ABORT有一个示例,当事务中的某些语句成功执行而某些语句XACT_ABORT设置为时失败OFF

    是的,但该示例中的语句不在块内TRY。块中的那些相同语句TRY仍将阻止执行导致错误的语句之后的任何语句,但假设XACT_ABORT是OFF,当控制权传递给CATCH块时,事务仍然在物理上有效,因为所有先前的更改确实发生而没有错误并且可以承诺,如果这是愿望的话,或者他们可以回滚。另一方面,如果XACT_ABORT是,ON那么任何先前的更改都会自动回滚,然后您可以选择: a) 发出ROLLBACK这主要是对这种情况的接受,因为事务已经回滚减去重置@@TRANCOUNT为0,或者 b) 得到一个错误。没有太多选择,是吗?

    这个难题的一个可能重要的细节在该文档中并不明显,SET XACT_ABORT即该会话属性,甚至该示例代码,自SQL Server 2000以来就已经存在(版本之间的文档几乎相同),早于TRY...CATCH构造在 SQL Server 2005 中引入。再次查看该文档并查看示例(没有)TRY...CATCH,使用XACT_ABORT ON导致事务立即回滚:没有“不可提交”的事务状态(请注意,在该文档中的所有“不可提交”事务状态SET XACT_ABORT)。

    我认为有理由得出以下结论:

    1. SQL Server 2005 中结构的引入TRY...CATCH产生了对新事务状态(即“不可提交”)和XACT_STATE()获取该信息的函数的需求。
    2. 只有当以下两个都为真时,签XACT_STATE()入一个块才有意义:CATCH
    3. XACT_ABORT是OFF(否则XACT_STATE()应该总是返回-1,@@TRANCOUNT这就是你所需要的)
    4. CATCH如果调用是嵌套的,则在块中或链上的某个地方有逻辑,这会进行更改(COMMIT甚至是任何 DML、DDL 等语句),而不是执行ROLLBACK. (这是一个非常非典型的用例)** 请参阅底部的注释,在 UPDATE 3 部分,关于 Microsoft 的非官方建议始终检查XACT_STATE()而不是@@TRANCOUNT,以及为什么测试表明他们的推理没有成功。
    5. SQL Server 2005 中引入的TRY...CATCH构造在很大程度上已经淘汰了XACT_ABORT ONsession 属性,因为它提供了对 Transaction 更大程度的控制(您至少可以选择COMMIT,前提是XACT_STATE()不返回-1)。
      另一种看待这个问题的方式是,在 SQL Server 2005 之前,与在每个语句之后进行检查相比,XACT_ABORT ON提供了一种在发生错误时停止处理的简单可靠的方法。@@ERROR
    6. 的文档示例代码XACT_STATE()是错误的,或者充其量是误导性的,因为它显示了检查XACT_STATE() = 1when XACT_ABORTis ON。

    长部分;-)

    是的,MSDN 上的示例代码有点令人困惑(另请参阅:@@TRANCOUNT (Rollback) vs. XACT_STATE);-)。而且,我觉得它具有误导性,因为它要么显示一些没有意义的东西(因为你要问的原因:你甚至可以在CATCH块中拥有一个“可提交”的交易XACT_ABORTis ON),或者即使它是可能的,它仍然专注于很少有人想要或需要的技术可能性,而忽略了人们更可能需要它的原因。

    如果 TRY 块内有足够严重的错误,则控制将传递到 CATCH。所以,如果我在 CATCH 中,我知道事务有问题,在这种情况下,唯一明智的做法就是回滚它,不是吗?

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

    • “足够严重的错误”:为了清楚起见,TRY...CATCH将捕获大多数错误。不会被捕获的列表在链接的 MSDN 页面上列出,在“不受 TRY…CATCH 构造影响的错误”部分下。

    • “如果我在 CATCH 中,我知道事务有问题”(添加了 em phas):如果“事务”是指您通过将语句分组到显式事务中确定的逻辑工作单元,那么很可能是的。我认为我们大多数 DB 人员会倾向于同意回滚是“唯一明智的做法”,因为我们可能对我们如何以及为什么使用显式事务有类似的看法,并设想哪些步骤应该构成一个原子单元工作的。

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

    鉴于上述两点,我们可能应该区分“不能”提交的事务和“不想”提交的事务。

    当XACT_STATE()返回 a1时,这意味着 Transaction 是“可提交的”,您可以在COMMITor之间进行选择ROLLBACK。您可能不想提交它,但如果出于某些难以想出的示例的原因您想要提交,至少您可以,因为事务的某些部分确实成功完成。

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

    但是,如果您在示例的顶部注意到,设置的设置会XACT_ABORT ON有所改变。您可能会遇到常规错误,这样做之后将在is和 XACT_STATE() 返回BEGIN TRAN时将控制权传递给 CATCH 块。但是,如果 XACT_ABORT 是,那么对于任何 'ol 错误,事务都会“中止”(即无效),然后将返回。在这种情况下,在块内检查似乎没用,因为它似乎总是返回when is 。XACT_ABORTOFF1ONXACT_STATE()-1XACT_STATE()CATCH-1XACT_ABORTON

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

    • 的 MSDN 页面TRY...CATCH,在“Uncommittable Transactions and XACT_STATE”部分下,说:

      当错误发生在 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 语句将失败。

    最适用的用法似乎是在链接服务器 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_ABORT是OFF出于遗留原因默认情况下,通常我们应该将其设置为ON.
    ...
    “...如果您遵循建议并在 SET XACT_ABORT ON 的情况下运行,则事务将永远失败。”

    在XACT_ABORT ON到处使用之前,我会问:这里到底得到了什么?我没有发现有必要这样做并且通常主张您应该仅在必要时使用它。无论您是否想ROLLBACK通过使用@Remus 的答案中显示的模板,或者我多年来一直使用的模板,基本上是相同的,但没有保存点,都可以轻松处理,如本答案所示(处理嵌套调用):

    我们是否需要在 C# 代码和存储过程中处理事务


    更新 2

    我做了更多的测试,这次是通过创建一个小的 .NET 控制台应用程序,在应用程序层中创建一个事务,在执行任何SqlCommand对象(即 via using (SqlTransaction _Tran = _Connection.BeginTransaction()) { ...)之前,以及使用批处理中止错误而不是仅仅一个语句-aborting 错误,发现:

    1. “不可提交的”事务是在大多数情况下已经回滚的事务(更改已撤消),但@@TRANCOUNT仍然 > 0。
    2. 当您有一个“不可提交的”事务时,您不能发出一个COMMIT,因为这会生成一个错误,指出该事务是“不可提交的”。您也不能忽略它/什么也不做,因为当批处理完成时会生成一个错误,说明该批处理以一个挥之不去的、不可提交的事务完成并且它将被回滚(所以,嗯,如果它无论如何都会自动回滚,为什么要抛出错误?)。因此,您必须发出一个明确的ROLLBACK,也许不是在立即CATCH块中,而是在批处理结束之前。
    3. 在一个TRY...CATCH构造中,when XACT_ABORTis OFF,如果发生在TRY块之外会自动终止事务的错误,例如批处理中止错误,将撤消工作但不会终止事务,将其保持为“不可提交”。发出 aROLLBACK更像是结束交易所需的形式,但工作已经回滚。
    4. 当XACT_ABORTis时ON,大多数错误都作为批处理中止,因此其行为与上面的项目符号点(#3)中描述的一样。
    5. XACT_STATE(),至少在一个CATCH块中,如果在错误发生时存在活动事务,则将显示-1批处理中止错误。
    6. XACT_STATE()1即使没有活动的事务,有时也会返回。如果@@SPID(除其他外)与SELECT一起在列表中XACT_STATE(),则XACT_STATE()在没有活动事务时将返回 1。此行为始于 SQL Server 2012,并存在于 2014 年,但我尚未在 2016 年测试过。

    考虑到以上几点:

    • XACT_STATE()鉴于第 4 点和第 5 点,由于大多数(或所有?)错误将导致事务“不可提交”,因此在何时检查CATCH块似乎完全没有意义,因为返回的值将始终为.XACT_ABORTON-1
    • Checking XACT_STATE() in the CATCH block when XACT_ABORT is OFF makes more sense because the return value will at least have some variation since it will return 1 for statement-aborting errors. However, if you code like most of us, then this distinction is meaningless since you will be calling ROLLBACK anyway simply for the fact that an error occurred.
    • If you find a situation that does warrant issuing a COMMIT in the CATCH block, then check the value of XACT_STATE(), and be sure to SET XACT_ABORT OFF;.
    • XACT_ABORT ON seems to offer little to no benefit over the TRY...CATCH construct.
    • I can find no scenario where checking XACT_STATE() provides a meaningful benefit over simply checking @@TRANCOUNT.
    • I can also find no scenario where XACT_STATE() returns 1 in a CATCH block when XACT_ABORT is ON. I think it is a documentation error.
    • Yes, you can roll-back a Transaction that you did not explicitly begin. And in the context of using XACT_ABORT ON, it's a moot point since an error happening in a TRY block will automatically roll-back the changes.
    • The TRY...CATCH construct has the benefit over XACT_ABORT ON in not automatically cancelling the whole Transaction, and hence allowing the Transaction (as long as XACT_STATE() returns 1) to be committed (even if this is an edge-case).

    Example of XACT_STATE() returning -1 when XACT_ABORT is OFF:

    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;
    

    UPDATE 3

    Related to item #6 in the UPDATE 2 section (i.e. possible incorrect value returned by XACT_STATE() when there is no active Transaction):

    • The odd / erroneous behavior started in SQL Server 2012 (so far tested against 2012 SP2 and 2014 SP1)

    • In SQL Server versions 2005, 2008, and 2008 R2, XACT_STATE() did not report expected values when used in Triggers or INSERT...EXEC scenarios: xact_state() cannot be used reliably to determine whether a transaction is doomed (archived page). However, in these 3 versions (I only tested on 2008 R2), XACT_STATE() does not incorrectly report 1 when used in a SELECT with @@SPID.

    • There is was a Connect bug filed against the behavior mentioned here but is closed as "By Design": XACT_STATE() can return an incorrect transaction state in SQL 2012 (link no longer valid due to incompetent and/or grossly negligent site migration ?). However, the test was done when selecting from a DMV and it was concluded that doing so would naturally have a system generated transaction, at least for some DMVs. It was also stated in the final response by MS that:

      Note that an IF statement, and also a SELECT without FROM, do not start a transaction.
      for example, running SELECT XACT_STATE() if you don't have a previously existing transaction will return 0.

      Those statements are incorrect given the following example:

        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
      
    • Hence, I filed a new Feedback bug:
      XACT_STATE() returns 1 when used in SELECT with some system variables but without FROM clause

    PLEASE NOTE that in the "XACT_STATE() can return an incorrect transaction state in SQL 2012" Connect item linked directly above, Microsoft (well, a representative of) states:

    @@trancount returns the number of BEGIN TRAN statements. It is thus not a reliable indicator of whether there is an active transaction. XACT_STATE() also returns 1 if there is an active autocommit transaction, and is thus a more reliable indicator of whether there is an active transaction.

    However, I can find no reason to not trust @@TRANCOUNT. The following test shows that @@TRANCOUNT does indeed return 1 in an auto-commit transaction:

    --- 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
    

    I also tested on a real table with a Trigger and @@TRANCOUNT within the Trigger did accurately report 1 even though no explicit Transaction had been started.

    • 8
  3. Remus Rusanu
    2016-02-09T00:15:44+08:002016-02-09T00:15:44+08:00

    I would approach this differently. XACT_ABORT_ON is a sledge hammer, you can use a more refined approach, see Exception handling and nested transactions:

    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
    

    This approach will rollback, when possible, only the work performed inside the TRY block, and restore state to the state before entering the TRY block. This way you can do complex processing, like iterating a cursor, w/o loosing all the work in case of an error. The only draw back is that, by using transaction savepoints, you are restricted from using anything that is incompatible with savepoints, like distributed transactions.

    • 7
  4. Hannah Vernon
    2016-02-08T17:35:50+08:002016-02-08T17:35:50+08:00

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

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

    回滚可能会在 TRY...CATCH 中失败的一种方式是,如果您没有明确启动事务。复制和粘贴代码块可能很容易导致这种情况。

    • 5

相关问题

  • 死锁的主要原因是什么,可以预防吗?

  • 我在索引上放了多少“填充”?

  • 是否有开发人员遵循数据库更改的“最佳实践”类型流程?

  • 如何确定是否需要或需要索引

  • 从 SQL Server 2008 降级到 2005

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve