it-swarm.cn

SQL事务错误:无法提交当前事务,并且不支持写入日志文件的操作

我遇到与 类似的问题,当前事务无法提交,并且不支持写入日志文件的操作 ,但我有一个后续问题。

那里的答案参考 在Transact-SQL中使用TRY ... CATCH ...

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

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

根据我在 中阅读的内容,在Transact-SQL中使用TRY ... CATCH ,我认为正在发生的事情是当引发异常时,使用 _XACT_ABORT_ 会导致交易“终止并回滚” ...然后_BEGIN CATCH_的第一行是盲目的尝试再次回滚。

我不知道为什么原始开发者启用了_XACT_ABORT_,所以我认为更好的解决方案(比删除它)是使用XACT_STATE()仅在发生事务时才回滚( _<>0_)。听起来合理吗?我想念什么吗?

另外,提到错误消息中的登录让我感到奇怪:是否还有另一个问题,可能与配置有关?在这种情况下我们使用RAISEERROR()是否会导致问题?在某种不可能记录日志的情况下,是否会记录该日志,因为错误消息暗示了这一点?

34
Adam Tuttle

您始终需要检查XACT_STATE(),与XACT_ABORT 设置。我有一个存储过程模板的示例,该模板需要在以下位置处理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
38
Remus Rusanu

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

首先,无论事务处于什么状态,您都可以始终回滚事务。因此,您只需要在COMMIT之前检查XACT_STATE,而无需回滚之前。

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

 IF @@TRANCOUNT > 0
      ROLLBACK TRANSACTION @transaction

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

这个问题来自去年,所以希望您现在已经解决了这个问题:-) Remus为您指出了正确的方向。

根据经验,当出现错误时,TRY将立即跳转到CATCH。然后,当您处于CATCH模式时,可以使用XACT_STATE来决定是否可以提交。但是,如果您始终想在回扣中回滚,则根本不需要检查状态。

15
Donna_123_a