web-dev-qa-db-ja.com

SQL ServerジョブでのトランザクションとTry-Catch

SQL Serverジョブの各ステップにはDML操作があります。何かがうまくいかない場合に更新/挿入が確実にロールバックされるようにするために、私は各ステップのデータ変更をTRY CATCHおよびTRANSACTIONブロックにラップしました:

BEGIN TRY
    BEGIN TRANSACTION

        [[INSERT/update statements]] ...

    IF @@TRANCOUNT > 0
    BEGIN
        COMMIT TRANSACTION
        PRINT 'Successful.'
    END

END TRY

BEGIN CATCH
    SELECT
        ERROR_NUMBER() AS ErrorNumber,
        ERROR_SEVERITY() AS ErrorSeverity,
        ERROR_STATE() AS ErrorState,
        ERROR_PROCEDURE() AS ErrorProcedure,
        ERROR_LINE() AS ErrorLine,
        ERROR_MESSAGE() AS ErrorMessage

    IF @@TRANCOUNT > 0
    BEGIN
        ROLLBACK TRANSACTION
        PRINT 'Unsuccessful.'
    END
END CATCH

エラーが発生した場合、データ操作が確実にロールバックされますか?または他の考慮事項を考慮する必要がありますか?

(構成などを使用して)それを行うより良い方法はありますか?

ありがとうございました。

9
Sky

例外処理とネストされたトランザクション のようなパターンをお勧めします。

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

このパターンは、catchブロックの XACT_STATE() をチェックして、 コミットできないトランザクション から保護します。

コミットできないトランザクションとXACT_STATE
TRYブロックで生成されたエラーにより現在のトランザクションの状態が無効になる場合、そのトランザクションはコミットできないトランザクションとして分類されます。通常、TRYブロックの外部でトランザクションを終了するエラーが発生すると、TRYブロックの内部でエラーが発生すると、トランザクションがコミットできない状態になります。コミットできないトランザクションは、読み取り操作またはROLLBACK TRANSACTIONのみを実行できます。トランザクションは、書き込み操作またはCOMMIT TRANSACTIONを生成するTransact-SQLステートメントを実行できません。トランザクションがコミットできないトランザクションとして分類されている場合、XACT_STATE関数は値-1を返します。バッチが終了すると、データベースエンジンはアクティブなコミットできないトランザクションをロールバックします。トランザクションがコミットできない状態になったときにエラーメッセージが送信されなかった場合、バッチが完了すると、エラーメッセージがクライアントアプリケーションに送信されます。これは、コミットできないトランザクションが検出され、ロールバックされたことを示しています。

あなたのコードは、0にできない場所で_@@TRANCOUNT_をチェックしています。成功を通知するために、情報を提供するPRINTメッセージとSELECT結果セットの混合を使用し、回復可能なエラーを処理しません。理想的には、例外はクライアント、この場合はエージェントジョブに伝播する必要があります(つまり、キャッチは再発生するはずです)。

7
Remus Rusanu

あなたが持っているものは私にはよさそうだ。もちろん、トランザクションをロールバックした後で、その情報を使用して何かを行うことをお勧めします。ログに書き込みます。

2
datagod