web-dev-qa-db-ja.com

SQL Serverのバージョンに応じて、RAISERRORまたはTHROWを選択することはできますか?

これが今の私のコードです:

BEGIN TRY
INSERT INTO TABLE (F1,F2,F3) 
VALUES ('1','2','3')
END TRY
BEGIN CATCH
;THROW
END CATCH

SQL 2008が搭載されたマシンで実行しない限り、うまく機能します。CATCHブロックでSQLバージョンをチェックし、それが2012以上の場合はTHROWを実行し、2008の場合はRAISERRORを実行します。構文エラー、それが可能かどうか疑問に思っています。このような単純なものでさえ、私にはうまくいきません。

BEGIN CATCH
IF ((SELECT SERVERPROPERTY('productversion')) >= 11) ;THROW
END CATCH

任意のアドバイスをいただければ幸いです。

11
thomasjbarrett

いいえ、これは不可能です。

これは以前のバージョンでは無効な構文であり、コンパイルエラーの原因になります。

パラメータなしのスローをキャッチ内に直接含める必要があるため、キャッチブロック内のTHROW内のEXECを非表示にすることもできません。

展開先のSQL Serverのバージョンに応じて、必要なコードバージョンを展開する必要があります(残念ながら、私が認識しているSSDTツールでは、これに対する適切なサポートはありません。コード行を選択的に含めることに相当するものはありません。条件付きコンパイル)

9
Martin Smith

たとえTHROWRAISERRORを交互に切り替えることが技術的に可能であったとしても、あなたが(おそらく)実際にこれを実行したくないことを指摘しておく必要があります。どうして?パラメータレスTHROWの非常に気の利いた機能は、同じメッセージ番号(つまり、Msg 8134Msg Xの代わりにX> = 50000)はそれらの間の唯一の違いではありません。THROWはバッチ中止されますが、RAISERRORはそうではありません。以下に示すように、これは重要な動作の違いになる可能性があります。

テスト設定

--DROP PROC ##Throw;
--DROP PROC ##RaisError;

GO
CREATE PROCEDURE ##Throw
AS
SET NOCOUNT ON;
BEGIN TRY
  SELECT 1/0 AS [DivideByZero];
END TRY
BEGIN CATCH
  THROW;
END CATCH;
SELECT 1 AS [AA];
GO

CREATE PROCEDURE ##RaisError
AS
SET NOCOUNT ON;
BEGIN TRY
  SELECT 1/0 AS [DivideByZero];
END TRY
BEGIN CATCH
  RAISERROR('test, yo!', 16, 1);
  -- RETURN; -- typically at end of CATCH block when using RAISERROR
END CATCH;
SELECT 2 AS [BB];
GO

テスト1

EXEC ##Throw;
SELECT 3 AS [CC];

戻り値:

"Results" Tab:

DivideByZero
{empty result set}

"Messages" Tab:

Msg 8134, Level 16, State 1, Procedure ##Throw, Line 38
Divide by zero error encountered.

テスト2

EXEC ##RaisError;
SELECT 4 AS [DD];

戻り値:

"Results" Tab:

DivideByZero
{empty result set}

BB
2

DD
4

"Messages" Tab:

Msg 50000, Level 16, State 1, Procedure ##RaisError, Line 45
test, yo!

公平を期すために、次のようにしてこの違いを隠すことができます。

  • 常にTHROWを使用してコードへのすべての呼び出しをTRY...CATCH構成内にラップします(以下に示します)
  • 決してTHROWの後にコードを配置しない(まあ、END CATCH;を除く)

テスト

BEGIN TRY
  EXEC ##Throw;
  SELECT 5 AS [EE];
END TRY
BEGIN CATCH
  SELECT ERROR_NUMBER() AS [ErrorNumber], ERROR_MESSAGE() AS [ErrorMessage];
END CATCH;
SELECT 6 AS [FF];
GO

戻り値:

"Results" Tab:

DivideByZero
{empty result set}

ErrorNumber     ErrorMessage
8134            Divide by zero error encountered.

FF
6

テスト4

BEGIN TRY
  EXEC ##RaisError;
  SELECT 7 AS [GG];
END TRY
BEGIN CATCH
  SELECT ERROR_NUMBER() AS [ErrorNumber], ERROR_MESSAGE() AS [ErrorMessage];
END CATCH;
SELECT 8 AS [HH];
GO

戻り値:

"Results" Tab:

DivideByZero
{empty result set}

ErrorNumber     ErrorMessage
50000           test, yo!

HH
8
4
Solomon Rutzky

私は Martin Smithの答え がほぼ100%正しいと信じています。

これを行う唯一の方法は動的SQLを使用することであり、すべてのtry/catchブロック(または、2つのバージョンのそれら)は、バージョンに応じて実行されます。

それを維持するのは悪夢です。しないでください。

SQL Serverのバージョンに基づいてSQLステートメントを実行する方法はありますか?

3
SqlZim