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

时间:2011-09-20 15:59:35

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

我遇到与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

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

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

  

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

显然,错误来自第二个捕获块

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

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

此外,提到登录错误消息让我想知道:还有其他问题,可能是配置?我们在这种情况下使用RAISEERROR()是否会导致问题?是否会记录,在某种情况下无法进行日志记录,因为错误消息暗示了?

4 个答案:

答案 0 :(得分:36)

您始终需要检查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

答案 1 :(得分:15)

上述讨论中存在一些误解。

首先,无论事务的状态如何,您都可以随时ROLLBACK事务。所以你只需要在COMMIT之前检查XACT_STATE,而不是在回滚之前检查。

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

 IF @@TRANCOUNT > 0
      ROLLBACK TRANSACTION @transaction

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

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

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

答案 2 :(得分:0)

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

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

答案 3 :(得分:-1)

在过程中发生完全相同的错误。 事实证明,运行该表的用户(在我们的情况下为技术用户)没有足够的权限来创建临时表。

EXEC sp_addrolemember'db_ddladmin','username_here';

成功了