Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

こんな感じの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

https://docs.microsoft.com/ja-jp/sql/t-sql/language-elements/return-transact-sql?view=sql-server-2016

クエリまたはプロシージャを無条件で終了します。 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

https://docs.microsoft.com/ja-jp/sql/t-sql/language-elements/throw-transact-sql?view=sql-server-2016

例外を発生させ、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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした