web-dev-qa-db-ja.com

SQL Server TRY..CATCHは終了エラーで再開します

私はマスターストアドプロシージャを完了まで実行することを試みています。マスターSPは子SPを呼び出して、互いに独立したモジュラータスクを実行します(つまり、1つが失敗した場合、他の実行を停止するべきではありません。これらの子SPの1つは失敗する可能性があり、なぜ失敗するのかは事前にわかりませんが、私が遭遇した1つのシナリオは、存在しないテーブルから選択しようとしていることです。そのエラーをログに記録してから、マスタースクリプトを続行します。存在しないテーブルからの選択は端末エラーであると思います。端末エラーでも続行する方法があるかどうか知りたいのですが。

(はい、私は情報スキーマに問い合わせてテーブルの存在を判断できることを知っていますが、防御的にコード化されていないエラーをキャッチしようとしています-今日はテーブルが欠落している可能性があり、明日は可能性がありますマスタースクリプトを悲鳴を上げる停止とする、私たちが考慮していない他の何かになります。)

コード例:

DROP TABLE IF EXISTS my_error_log;
CREATE TABLE my_error_log (
    error_time DATETIME,
    error_msg VARCHAR(MAX)
);
GO

CREATE OR ALTER PROCEDURE failProc
AS
BEGIN
    BEGIN TRY
        -- This one fails, is caught, and continues...
        -- SELECT 1/0;
        -- This one fails, is caught, and terminates the master script...
        SELECT * FROM non_existent_table;
    END TRY
    BEGIN CATCH
        INSERT INTO my_error_log
        SELECT CURRENT_TIMESTAMP, ERROR_MESSAGE();
    END CATCH
END;
GO  

CREATE OR ALTER PROCEDURE masterFailProc
AS
BEGIN
    SET XACT_ABORT OFF;
    BEGIN TRY
        INSERT INTO my_error_log SELECT CURRENT_TIMESTAMP, 'start';
        EXEC failProc;
        EXEC failProc;
        EXEC failProc;
        INSERT INTO my_error_log SELECT CURRENT_TIMESTAMP, 'end';
    END TRY
    BEGIN CATCH
        INSERT INTO my_error_log SELECT CURRENT_TIMESTAMP, 'fatal error';
    END CATCH
END;
GO  

EXEC masterFailProc
GO

SELECT * FROM my_error_log;
GO

DROP PROCEDURE masterFailProc;
DROP PROCEDURE failProc;
DROP TABLE my_error_log;
2
e_i_pi

期待したほどエレガントではない解決策を見つけましたが、うまくいきました。誰かがもっと良いものがあれば聞いてみたいです。

masterFailProcの問題は、個々のEXEC呼び出しではなく、スクリプト全体をTRY..CATCHブロックでラップしていることです。定義が以下のように変更された場合、スクリプト全体を終了するのではなく、execごとにエラーをログに記録します。

CREATE OR ALTER PROCEDURE masterFailProc
AS
BEGIN
    SET XACT_ABORT OFF;
    BEGIN TRY
        INSERT INTO my_error_log SELECT CURRENT_TIMESTAMP, 'start';
        BEGIN TRY EXEC failProc; END TRY BEGIN CATCH /* log error */ END CATCH;
        BEGIN TRY EXEC failProc; END TRY BEGIN CATCH /* log error */ END CATCH;
        BEGIN TRY EXEC failProc; END TRY BEGIN CATCH /* log error */ END CATCH;
        INSERT INTO my_error_log SELECT CURRENT_TIMESTAMP, 'end';
    END TRY
    BEGIN CATCH
        INSERT INTO my_error_log SELECT CURRENT_TIMESTAMP, 'fatal error';
    END CATCH
END;
GO  
2
e_i_pi