【问题标题】:Do you need to call SET XACT_ABORT only when starting a transaction?仅在开始事务时才需要调用 SET XACT_ABORT 吗?
【发布时间】:2011-04-18 18:56:31
【问题描述】:

我想澄清一下这个答案 -> Nested stored procedures containing TRY CATCH ROLLBACK pattern?

以下是来自上述链接的代码示例

 1 CREATE PROCEDURE [Name]
 2 AS
 3 SET XACT_ABORT, NOCOUNT ON
 4
 5 DECLARE @starttrancount int
 6
 7 BEGIN TRY
 8    SELECT @starttrancount = @@TRANCOUNT
 9
10    IF @starttrancount = 0
11        BEGIN TRANSACTION
12
13       [...Perform work, call nested procedures...]
14
15    IF @starttrancount = 0 
16        COMMIT TRANSACTION
17 END TRY
19 BEGIN CATCH
20    IF XACT_STATE() <> 0 AND @starttrancount = 0 
21        ROLLBACK TRANSACTION
22    RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
23 END CATCH
24 GO

假设它是从启动事务的旧存储过程中调用的。 在这种情况下,这个 proc 不会启动它自己的事务,但会改变调用的 XACT_ABORT 状态。

所以,我有几个问题。

  • 当前 XACT_ABORT 是否处于活动状态 仅 proc,或整个通话 堆栈?
  • 如果我想重构一个 proc 来使用 SET XACT_ABORT ON,我需要配对吗 SET XACT_ABORT OFF?对于遗留代码,这是最安全的方法吗?

以下是修改后的示例,它有条件地打开 XACT_ABORT 并将其与在 proc 退出时关闭它配对

CREATE PROCEDURE [Name]
AS
SET NOCOUNT ON

DECLARE @starttrancount int

BEGIN TRY
   SELECT @starttrancount = @@TRANCOUNT

   IF @starttrancount = 0
   BEGIN
      SET XACT_ABORT ON
      BEGIN TRANSACTION
   END

      [...Perform work, call nested procedures...]

   IF @starttrancount = 0 
   BEGIN
      COMMIT TRANSACTION
      SET XACT_ABORT OFF
   END
END TRY
BEGIN CATCH
   IF XACT_STATE() <> 0 AND @starttrancount = 0 
   BEGIN
       ROLLBACK TRANSACTION
       SET XACT_ABORT OFF
   END
   RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
GO

【问题讨论】:

    标签: sql-server


    【解决方案1】:

    据我所知,子批处理的执行结束时会恢复此设置的先前值。

    SET NOCOUNT ON;
    
    BEGIN TRAN
    
    SELECT CASE
             WHEN 16384 & @@OPTIONS = 16384
               THEN 'XACT_ABORT IS ON'
             ELSE ' XACT_ABORT IS OFF'
           END
    
    CREATE TABLE #T
      (
         C INT
      )
    
    SET XACT_ABORT OFF
    
    INSERT INTO #T
    VALUES      (1)
    
    EXEC ('SET XACT_ABORT ON; 
          INSERT INTO #T VALUES(2);
            SELECT CASE
                        WHEN 16384 & @@OPTIONS = 16384
                        THEN ''XACT_ABORT IS ON''
                        ELSE '' XACT_ABORT IS OFF''
                    END 
          ')
    
    
    INSERT INTO #T
    VALUES      (1 / 0)
    
    /*If XACT_ABORT was on we would never get here but we do!*/
    COMMIT
    
    SELECT *
    FROM   #T
    
    DROP TABLE #T
    
    SELECT CASE
             WHEN 16384 & @@OPTIONS = 16384
               THEN 'XACT_ABORT IS ON'
             ELSE ' XACT_ABORT IS OFF'
           END 
    

    返回

    ------------------
     XACT_ABORT IS OFF
    
    
    ------------------
    XACT_ABORT IS ON
    
    Msg 8134, Level 16, State 1, Line 31
    Divide by zero error encountered.
    The statement has been terminated.
    C
    -----------
    1
    2
    
    
    ------------------
     XACT_ABORT IS OFF
    

    【讨论】:

    • 检查一下,这是否意味着以下答案不正确(即我阅读 Jimbo 的答案与此答案相矛盾;但两者都有赞成票......假设你的答案是正确的,因为你的声誉要高得多)。
    【解决方案2】:

    XACT_ABORT 是仅对当前 proc 有效,还是对整个调用堆栈有效? -- 设置为 ON 时,从该点开始对所有连接都处于活动状态

    如果我想重构一个 proc 以使用 SET XACT_ABORT ON,我需要将它与 SET XACT_ABORT OFF 配对吗?
    -- 如果这是你想要的行为,是的 - 这意味着你不会从遗留代码中获得新的行为。

    对于遗留代码,这是最安全的方法吗? -- 为了尽量减少对其他存储过程的影响,可以

    【讨论】:

      【解决方案3】:

      在我看来,它的作用域仅限于 SET 的存储过程,并在之后重置:

      如果 SET 语句在存储过程或触发器中运行,则 SET 选项的值会在存储过程或触发器返回控制权后恢复。此外,如果您在使用 sp_executesql 或 EXECUTE 运行的动态 SQL 字符串中指定 SET 语句,则在控制从您在动态 SQL 字符串中指定的批处理返回后恢复 SET 选项的值。

      https://docs.microsoft.com/en-us/sql/t-sql/statements/set-statements-transact-sql?view=sql-server-ver15#considerations-when-you-use-the-set-statements

      从那个页面我不清楚嵌套过程中的行为是什么。

      【讨论】:

        猜你喜欢
        • 2015-12-19
        • 1970-01-01
        • 1970-01-01
        • 2017-08-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-25
        相关资源
        最近更新 更多