你需要在开始事务时调用SET XACT_ABORT吗?

4
我想要对这个回答进行澄清 -> 嵌套的存储过程包含TRY CATCH ROLLBACK模式? 下面是来自上述链接的代码示例。
 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

假设它是从启动事务的旧存储过程中调用的。在这种情况下,此过程将不会启动自己的事务,但会更改调用的XACT_ABORT状态。
因此,我有几个问题:
  • XACT_ABORT仅适用于当前过程还是整个调用堆栈?
  • 如果我想重构一个过程以使用SET XACT_ABORT ON,是否需要与SET XACT_ABORT OFF配对?这是传统代码中最安全的方法吗?
下面是修改后的示例,它有条件地打开XACT_ABORT,并在过程退出时关闭它。
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
3个回答

4
据我所见,当子批处理的执行结束时,它会恢复此设置的先前值。
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

1
请确认,这是否意味着下面的答案是不正确的(即根据我的理解,Jimbo的答案与此答案相矛盾;但两者都有赞同...假设您的答案是正确的,因为您的声望显著更高)。 - JohnLBevan

0

XACT_ABORT是否仅对当前过程有效,还是对整个调用堆栈有效? -- 当设置为ON时,它从那一点开始对该连接的所有操作都有效。

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

这是遗留代码最安全的做法吗? -- 为了最小化对其他存储过程的影响,是的。


0

在我看来,它的作用范围仅限于存储过程中的SET语句,并在之后重置:

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

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

从那个页面上看,嵌套过程的行为对我来说不太清楚。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接