2011-09-20 443 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,那麼你根本不需要檢查狀態。