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 
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](https://qiita-image-store.s3.amazonaws.com/0/106639/a0ffd0c3-c112-caa2-10f0-05d6fa1ede17.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は全然やってこなかったので、本当にこの解決法でいいのかどうかすらよくわからない。
こんな乱暴じゃない解決法があるのであれば知りたいところ。。。