【问题标题】:Insert in catch block causes error: The current transaction cannot be committed and cannot support operations that write to the log file插入 catch 块导致错误:当前事务无法提交,无法支持写入日志文件的操作
【发布时间】:2022-01-05 16:28:31
【问题描述】:

我有两个过程,一个外部过程和一个内部过程,我想了解错误处理的行为。内部过程引发错误并试图将 catch 块中的某些内容插入到表中。之后引发错误,传递给外部过程,然后回滚事务。

我试图了解为什么我的代码会抛出错误消息:

Msg 50000, Level 11, State 1, Procedure dbo.OuterProcedure, Line 21 [Batch Start Line 9]
The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.

我希望收到以下消息:

Msg 50000, Level 11, State 1, Procedure dbo.OuterProcedure, Line 21 [Batch Start Line 9]
Error converting data type varchar to numeric.

我知道问题来自内部过程中的 catch 块,它发生是因为我试图在引发错误之前将某些内容插入到我的日志表中。当我切换这些语句或删除插入时,我会收到实际的错误消息。我也知道在内部过程和无论如何都回滚的事务中进行日志记录是不明智的。

即使 XACT_ABORT 设置为关闭,我想了解是什么导致此交易成为“注定”的交易。

完整代码:

我的主要程序:

CREATE PROCEDURE [dbo].[OuterProcedure]
AS
BEGIN
    SET XACT_ABORT OFF;

    BEGIN TRY
        BEGIN TRANSACTION ;

        -- do other stuff 

        EXEC [dbo].[innerprocedure];

        -- do other stuff 

        COMMIT TRANSACTION ;
    END TRY

    BEGIN CATCH
        ROLLBACK TRANSACTION;

        DECLARE @ErrText NVARCHAR(2000);
        SET @ErrText = ISNULL(ERROR_MESSAGE(), 'nothing')
        RAISERROR(@ErrText, 11, 1) WITH NOWAIT
    END CATCH;

END;

我的内心程序:

CREATE PROCEDURE [dbo].[InnerProcedure]
AS
BEGIN
    SET XACT_ABORT OFF;
    SET NOCOUNT ON;
    
    BEGIN TRY
    -- do other stuff 

    -- provoke error
        SELECT 
              CASE
                  WHEN 1 = 0
                  THEN 0.0
                  ELSE ''
              END;

    -- do other stuff 
    END TRY
    BEGIN CATCH

        DECLARE @ErrText NVARCHAR(2000);

        SELECT 
              @ErrText = ISNULL(ERROR_MESSAGE(), 'nothing');

        INSERT INTO [dbo].[logtable]
        ( 
              [Message]
            , [ErrNr]
        ) 
        VALUES
        ( @ErrText
        , -1
        );

        RAISERROR(@LogText, 11, 0) WITH NOWAIT;
    END CATCH;
END;

【问题讨论】:

  • 以上内容不太现实。为什么在外部过程中没有任何事务性的显式事务?事务应该在内部查询中。当您收到错误时,会将INSERT 写入表logtable,然后当错误到达外部过程时,它会被it's 错误处理程序回滚。
  • 你也应该使用THROWRAISERROR 不尊重 SET XACT_ABORT,根据 documentationRAISERROR 语句不支持 SET XACT_ABORT。新应用程序应使用 THROW 而不是 RAISERROR。"
  • 我知道这些程序是不现实的,我只是让它们找出并重现我在更复杂的查询中遇到的实际问题。外部过程仅将内部过程称为许多其他步骤之一,并且错误“自然”发生。我将在代码中指出它以提供进一步的清晰度。
  • 不过,这些都不会改变我的观点,@Chris。外部程序将ROLLBACKINSERT放入表dbo.LogTable;使INSERT 毫无意义,并且您正在使用一个函数 (RAISERROR),该函数被专门记录为不尊重您正在使用的功能 (SET XACT_ABORT)。
  • 我完全理解你的观点并且完全同意!一开始就是无意义的代码。我只是不明白为什么即使我没有提交我的事务也会抛出上述错误(无法提交当前事务)。使用 THROW 而不是 RAISEERROR 不会改变行为。

标签: sql-server tsql error-handling transactions try-catch


【解决方案1】:

我想了解是什么让这笔交易“注定失败” 即使 XACT_ABORT 设置为关闭,也可以进行事务处理。

XACT_STATE() 在 catch 块中是 -1,因此事务注定失败。

  SELECT 
      CASE
          WHEN 1 = 0
          THEN 0.0
          ELSE ''
      END;

抛出错误

将数据类型 varchar 转换为数值时出错。

“大多数转换错误”是错误类别中Erland Sommarskog puts 的错误类型之一。

Batch Abortion with Rollback 这是 SQL Server 最强烈的反应 可能会导致用户错误。这些是中止执行的错误 发现堆栈上是否没有 CATCH 处理程序并且它们也会回滚 任何未结交易。如果有一个 CATCH 处理程序,则错误是 被捕获,但任何打开的事务都注定失败并且必须回滚。 无论 XACT_ABORT 是 ON 还是 OFF,其行为都是相同的

错误行为的分类有点神秘、没有记录且不直观。阅读他的文章了解更多详情。

【讨论】:

    猜你喜欢
    • 2011-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多