【问题标题】:SQL Transaction Error: The current transaction cannot be committed and cannot support operations that write to the log fileSQL 事务错误:当前事务无法提交,无法支持写入日志文件的操作
【发布时间】:2011-11-21 05:39:28
【问题描述】:

我遇到了与The current transaction cannot be committed and cannot support operations that write to the log file 类似的问题,但我有一个后续问题。

那里的答案引用了Using TRY...CATCH in Transact-SQL,我稍后会回到...

我的代码(当然是继承的)具有简化形式:

SET NOCOUNT ON
SET XACT_ABORT ON

CREATE TABLE #tmp

SET @transaction = 'insert_backtest_results'
BEGIN TRANSACTION @transaction

BEGIN TRY

    --do some bulk insert stuff into #tmp

END TRY

BEGIN CATCH
    ROLLBACK TRANSACTION @transaction
    SET @errorMessage = 'bulk insert error importing results for backtest '
        + CAST(@backtest_id as VARCHAR) +
        '; check backtestfiles$ directory for error files ' + 
        ' error_number: ' + CAST(ERROR_NUMBER() AS VARCHAR) + 
        ' error_message: ' + CAST(ERROR_MESSAGE() AS VARCHAR(200)) +
        ' error_severity: ' + CAST(ERROR_SEVERITY() AS VARCHAR) +
        ' error_state ' +  CAST(ERROR_STATE() AS VARCHAR) + 
        ' error_line: ' + CAST(ERROR_LINE() AS VARCHAR)
    RAISERROR(@errorMessage, 16, 1)
    RETURN -666
END CATCH

BEGIN TRY

    EXEC usp_other_stuff_1 @whatever

    EXEC usp_other_stuff_2 @whatever

    -- a LOT of "normal" logic here... inserts, updates, etc...

END TRY

BEGIN CATCH

    ROLLBACK TRANSACTION @transaction
    SET @errorMessage = 'error importing results for backtest '
        + CAST(@backtest_id as VARCHAR) +
        ' error_number: ' + CAST(ERROR_NUMBER() AS VARCHAR) + 
        ' error_message: ' + CAST(ERROR_MESSAGE() AS VARCHAR(200)) +
        ' error_severity: ' + CAST(ERROR_SEVERITY() AS VARCHAR) +
        ' error_state ' +  CAST(ERROR_STATE() AS VARCHAR) + 
        ' error_line: ' + CAST(ERROR_LINE() AS VARCHAR)
    RAISERROR(@errorMessage, 16, 1)
    RETURN -777

END CATCH

RETURN 0

我认为我有足够的信息来使用它并自己解决它......不幸的是,重现错误几乎是不可能的。所以我希望在这里提问有助于澄清我对问题和解决方案的理解。

这个存储过程会间歇性地抛出类似这样的错误:

回测导入结果错误 9649 error_number: 3930 error_message: 当前事务无法提交,无法支持写入日志文件的操作。回滚事务。 error_severity: 16 error_state 1 error_line: 217

显然错误来自第二个 catch 块

根据我在Using TRY...CATCH in Transact-SQL 中读到的内容,我认为发生的情况是,当抛出异常时,使用XACT_ABORT 会导致事务“终止并回滚”......然后BEGIN CATCH 的第一行是在盲目地尝试再次回滚。

我不知道为什么最初的开发者启用了XACT_ABORT,所以我认为更好的解决方案(比删除它)是使用XACT_STATE() 仅在有事务时回滚(<>0 )。这听起来合理吗?我错过了什么吗?

此外,在错误消息中提到登录让我想知道:是否还有其他问题,可能与配置有关?我们在这种情况下使用RAISEERROR() 是否会导致问题?是否会像错误消息所暗示的那样在某种无法记录的情况下记录下来?

【问题讨论】:

    标签: sql-server sql-server-2005 transactions


    【解决方案1】:

    您始终需要检查XACT_STATE(),与XACT_ABORT 设置无关。我有一个存储过程模板示例,需要在 Exception handling and nested transactions 处处理 TRY/CATCH 上下文中的事务:

    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
    

    【讨论】:

    • 您的模板假定 try 块内的事务;我们在 1 个事务中有多个 try 块。
    • @Adam:这是关于你如何处理XACT_STATE 和 CATCH 块中的事务。使用这个模板,您可以在一个事务中拥有多个 try 块。这个想法是了解事务和捕获块如何交互,并且作为奖励,您还可以获得嵌套事务和保存点的处理,这在批处理中非常有用,因为它可以恢复批处理的其余部分,即使一个输入失败。
    • 这个答案值得更多的支持。该模板非常清晰有用,适用性超出了这个问题。
    • @DevinLamothe 因为稍后会再次检查该变量
    • 我们有一个 proc 在catch 中记录了一些错误,但它本身没有明确的事务概念。它是从另一个包裹着事务的 proc 调用的(特别是 tSQLt 单元测试)。第一个 proc 中的错误日志出现错误“当前事务无法提交......”。您是否建议我们让第一个 proc 意识到可以在事务中使用它来解决此问题?谢谢
    【解决方案2】:

    上面的讨论存在一些误解。

    首先,您始终可以回滚事务...无论事务处于何种状态。因此,您只需在 COMMIT 之前检查 XACT_STATE,而不是在回滚之前。

    至于代码中的错误,您需要将事务放在 TRY 中。然后在你的 CATCH 中,你应该做的第一件事是:

     IF @@TRANCOUNT > 0
          ROLLBACK TRANSACTION @transaction
    

    然后,在上述声明之后,您可以发送电子邮件或任何需要的东西。 (仅供参考:如果您在回滚之前发送电子邮件,那么您肯定会收到“无法...写入日志文件”错误。)

    这个问题是去年的问题,所以我希望你现在已经解决了这个问题:-) 莱姆斯为你指出了正确的方向。

    根据经验……当出现错误时,TRY 会立即跳转到 CATCH。然后,当您在 CATCH 中时,您可以使用 XACT_STATE 来决定您是否可以提交。但是如果你总是想在 catch 中 ROLLBACK,那么你根本不需要检查状态。

    【讨论】:

    • 虽然主题非常微妙; “你总是可以回滚交易”的说法是不正确的——至少使用给出的例子。如果 XACT_ABORT 标志为 ON,则应在回滚之前检查 XACT_STATE。例如。如果在 XACT_ABORT ON 的情况下创建命名事务,则随后回滚命名事务,如您的示例所示,将导致错误。在这种情况下,必须检查 XACT_STATE 以确定要执行的命令。如图所示,简单地检查@@TRANCOUNT 将导致错误。
    【解决方案3】:

    我在更新已启用触发器的表中的记录时遇到此错误。 例如 - 我在表“Table1”上触发了“Trigger1”。 当我尝试使用更新查询更新“Table1”时 - 它会引发相同的错误。这是因为如果您在查询中更新超过 1 条记录,则 'Trigger1' 将引发此错误,因为如果在同一个表上启用它,它不支持更新多个条目。 我尝试在更新之前禁用触发器,然后执行更新操作,它完成了,没有任何错误。

    DISABLE TRIGGER Trigger1 ON Table1;
    Update query --------
    Enable TRIGGER Trigger1 ON Table1;
    

    【讨论】:

      【解决方案4】:

      我遇到了与上述类似的问题,并且收到了相同的错误消息。上面的答案很有帮助,但不是我需要的,这实际上有点简单。

      我有一个结构如下的存储过程:

      SET XACT_ABORT ON
      
      BEGIN TRY
      
          --Stored procedure logic
          
          BEGIN TRANSACTION
            --Transaction logic
          COMMIT TRANSACTION
      
          --More stored procedure logic
      
      END TRY
      
      BEGIN CATCH
      
          --Handle errors gracefully
      
      END CATCH
      

      TRY...CATCH 用于处理存储过程逻辑中的错误。过程中只有一部分包含事务,如果在此期间发生错误,CATCH 块不会接收到它,但会出现 SQL 事务错误消息。

      通过添加另一个 TRY...CATCH 包装器解决了这个问题,该包装器将 ROLLBACK 事务和 THROW 错误。这意味着这一步中的任何错误都可以在主 CATCH 块中得到妥善处理,就像存储过程的其余部分一样。

      SET XACT_ABORT ON
      
      BEGIN TRY
      
          --Stored procedure logic
          
          BEGIN TRY
            BEGIN TRANSACTION;
              --Transaction logic
            COMMIT TRANSACTION;
          END TRY
          BEGIN CATCH
            ROLLBACK;
            THROW;
          END CATCH
      
          --More stored procedure logic
      
      END TRY
      
      BEGIN CATCH
      
          --Handle errors gracefully
      
      END CATCH
      

      【讨论】:

        【解决方案5】:

        在程序中出现完全相同的错误。 事实证明,运行它的用户(在我们的例子中是技术用户)没有足够的权限来创建临时表。

        EXEC sp_addrolemember 'db_ddladmin', 'username_here';

        成功了

        【讨论】:

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