2011-09-20 442 views
26

我遇到类似问题The current transaction cannot be committed and cannot support operations that write to the log file,但我有后续问题。SQL事务错误:无法提交当前事务并且不能支持写入日志文件的操作

有引用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 

我想我有足够的信息来玩弄它并自己弄清楚......不幸的是,再现错误证明是不可能的。所以我希望在这里询问将有助于澄清我对问题和解决方案的理解。

此存储过程,间歇性,抛象这样的错误之一:

error importing results for backtest 9649 error_number: 3930 error_message: The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction. 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()来解决问题?在记录不可能的情况下,是否记录了错误信息?

回答

27

您总是需要检查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 
+0

您的模板假定try块内的事务;我们在1个事务中有多个try块。 –

+0

@Adam:这是关于如何处理'XACT_STATE'和CATCH块中的事务。您可以使用这个模板在一个事务中拥有多个try块。这个想法是了解事务和catch块如何相互作用,并且作为奖励,您还可以处理嵌套事务和savepoints,这在批处理中非常有用,因为它可以恢复批处理的其余部分,即使只有一个进入失败了。 –

+0

我已经提前将回滚语句包装在一个'if XACT_STATE()<> 0'中,但只有时间会告诉我们是否为我们解决了这个问题。猜猜我会继续,现在接受你的答案。 –

12

有在上述讨论中的几个误区。

首先,您可以随时ROLLBACK一个事务...无论事务处于什么状态。所以你只需要在COMMIT之前检查XACT_STATE,而不是在回滚之前。

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

IF @@TRANCOUNT > 0 
     ROLLBACK TRANSACTION @transaction 

然后,上面的语句之后,那么你就可以发送电子邮件或任何需要。 (仅供参考:如果您在回滚之前发送电子邮件,那么您一定会得到“无法...写入日志文件”错误。)

此问题来自去年,所以我希望您通过解决此问题现在:-) Remus指出你的方向是正确的。

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