web-dev-qa-db-ja.com

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

したがって、明らかにエラーは2番目の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

次に、上記のステートメントの後、メールまたは必要なものを送信できます。 (参考:ロールバックの前にメールを送信すると、「ログファイルに書き込むことができません...」エラーが必ず表示されます。)

この問題は昨年のものでしたので、今までにこれを解決していただければ幸いです。

経験則として...エラーが発生すると、TRYはすぐにCATCHにジャンプします。次に、CATCHにいるときに、XACT_STATEを使用してコミットできるかどうかを決定できます。ただし、キャッチで常にロールバックする場合は、状態をチェックする必要はまったくありません。

15
Donna_123_a