Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
4
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

SQLServerでTRY-CATCHしちゃうと呼び出し側でエラー判別しにくい

こんな感じのSQLをファイルにし、BATファイルにてsqlcmdを使って呼び出した。

[Sample.sql]
    BEGIN TRY
        -- トランザクション開始
        BEGIN TRANSACTION;

        -- クリア
        DELETE FROM TAEGET_TABLE;

        INSERT INTO 
            TAEGET_TABLE
            SELECT
                FT.CD,
                FT.FIELD1,
                FT.FIELD2
            FROM
                FROM_TABLE FT

        -- コミット
        COMMIT TRANSACTION;
    END TRY

    BEGIN CATCH 
        -- エラー情報
        SELECT
            ' [ERR-LINE] ' + CONVERT(VARCHAR, IsNull(ERROR_LINE(),''))
            + ' [ERR-MESSAGE] ' + IsNull(ERROR_MESSAGE(),'')
            + ' [ERR-NUMBER] ' +  CONVERT(VARCHAR, IsNull(ERROR_NUMBER(),''))
            + ' [ERR-PROCEDURE] ' +  CONVERT(VARCHAR, IsNull(ERROR_PROCEDURE(),''))
            + ' [ERR-SEVERITY] ' +  CONVERT(VARCHAR, IsNull(ERROR_SEVERITY(),''))
            + ' [ERR-STATE] ' +  CONVERT(VARCHAR, IsNull(ERROR_STATE(),''));

        -- ロールバック
        ROLLBACK TRANSACTION;

    END CATCH;
<Sample.bat>
sqlcmd %DB_AUTH_SQLCMD% -S %DB_SERVERNAME% -d %DB_DBNAME% -b -i Sample.sql
if %ERRORLEVEL% NEQ 0 goto ERROR

※環境変数はあらかじめ設定されているものとします

しかしこれだと%ERRORLEVEL%でエラーを判別しようと思っても上手くいかない。
まあTRY-CATCHで握りつぶしてしまっているのだからわからなくもない。

しかしSQL的にはエラーなのでエラールート処理へ進んでいきたい。

ERROR_NUMBER

調べてみるとこういう関数がある
https://docs.microsoft.com/ja-jp/sql/t-sql/functions/error-number-transact-sql?view=sql-server-2016

この関数は、TRY...CATCH 構造の CATCH ブロックが実行される原因となったエラーのエラー番号を返します。

すごいやないかと思ったが、別にこの関数はエラー番号を返してくれるわけでもなく。
そもそもCATCHの中で使ってたし。

RETURN

クエリまたはプロシージャを無条件で終了します。 RETURN は即時に実行され、完了します。また、プロシージャ、バッチ、またはステートメント ブロックを終了する任意の位置で使用できます。 RETURN の後に続くステートメントは実行されません。

難しく考えることなくCATCHの中でRETRUNしてコード値を適当に返せばいいやん。

こうか?

    BEGIN TRY
        -- トランザクション開始
        BEGIN TRANSACTION;

        -- クリア
        DELETE FROM TAEGET_TABLE;

        INSERT INTO 
            TAEGET_TABLE
            SELECT
                FT.CD,
                FT.FIELD1,
                FT.FIELD2
            FROM
                FROM_TABLE FT

        -- コミット
        COMMIT TRANSACTION;

        RETURN 0
    END TRY

    BEGIN CATCH 
        -- エラー情報
        SELECT
            ' [ERR-LINE] ' + CONVERT(VARCHAR, IsNull(ERROR_LINE(),''))
            + ' [ERR-MESSAGE] ' + IsNull(ERROR_MESSAGE(),'')
            + ' [ERR-NUMBER] ' +  CONVERT(VARCHAR, IsNull(ERROR_NUMBER(),''))
            + ' [ERR-PROCEDURE] ' +  CONVERT(VARCHAR, IsNull(ERROR_PROCEDURE(),''))
            + ' [ERR-SEVERITY] ' +  CONVERT(VARCHAR, IsNull(ERROR_SEVERITY(),''))
            + ' [ERR-STATE] ' +  CONVERT(VARCHAR, IsNull(ERROR_STATE(),''));

        -- ロールバック
        ROLLBACK TRANSACTION;
        RETURN -1

    END CATCH;

どうやらコトはそう簡単ではないらしい。

image.png

「このコンテキストでは、戻り値を返すRETURNステートメントは使用できません」

調べてみるとこういう使いかただとRETRUNは使えないらしい。
※正直この辺はちょっとよく理解できていない。

THROW

例外を発生させ、TRY...CATCH 構造の CATCH ブロックに実行制御を移します。

例外を握りつぶしてしまっているのがダメならば、そのままTHROWでいいのでは?

こんな感じ。

    BEGIN TRY
        -- トランザクション開始
        BEGIN TRANSACTION;

        -- クリア
        DELETE FROM TAEGET_TABLE;

        INSERT INTO 
            TAEGET_TABLE
            SELECT
                FT.CD,
                FT.FIELD1,
                FT.FIELD2
            FROM
                FROM_TABLE FT

        -- コミット
        COMMIT TRANSACTION;
    END TRY

    BEGIN CATCH 
        -- エラー情報
        SELECT
            ' [ERR-LINE] ' + CONVERT(VARCHAR, IsNull(ERROR_LINE(),''))
            + ' [ERR-MESSAGE] ' + IsNull(ERROR_MESSAGE(),'')
            + ' [ERR-NUMBER] ' +  CONVERT(VARCHAR, IsNull(ERROR_NUMBER(),''))
            + ' [ERR-PROCEDURE] ' +  CONVERT(VARCHAR, IsNull(ERROR_PROCEDURE(),''))
            + ' [ERR-SEVERITY] ' +  CONVERT(VARCHAR, IsNull(ERROR_SEVERITY(),''))
            + ' [ERR-STATE] ' +  CONVERT(VARCHAR, IsNull(ERROR_STATE(),''));

        -- ロールバック
        ROLLBACK TRANSACTION;
        THROW;

    END CATCH;

とりあえずこれでやりたかったことは実現できそうな感じにはなった。

終わりに

SQLは全然やってこなかったので、本当にこの解決法でいいのかどうかすらよくわからない。
こんな乱暴じゃない解決法があるのであれば知りたいところ。。。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
4
Help us understand the problem. What are the problem?