web-dev-qa-db-ja.com

ストアドプロシージャがTRY / CATCH内で失敗した場合、出力パラメータは設定されません

SQL Server 2008(2014年も同様)。出力パラメータを持つプロシージャを考えてみましょう。この手順ではエラーが発生する可能性があります(次の例ではエラーが発生します)。 TRY/CATCHブロック内でプロシージャを呼び出す場合、出力パラメーターの動作は同じではないことに注意してください。

例:

create procedure test_output @result varchar(10) output
as
begin
    set @result = 'hello'
    raiserror('This is an error', 16,1)
    set @result = 'error'
end

手順を簡単な方法で起動した場合:

declare @res1 varchar(10)
exec test_output @result = @res1 out
print 'Result is: '+ isnull(@res1, 'empty')

私たちは得ます(そして私はそれで大丈夫です):

メッセージ50000、レベル16、状態1、プロシージャtest_output、行7 [バッチ開始行12]
これはエラーです
結果は次のとおりです:エラー

プロシージャがtry/catchブロックにある場合:

declare @res2 varchar(10)
declare @error_message varchar(max)
begin try
    exec test_output @result = @res2 out
end try
begin catch
    set @error_message = error_message()
    raiserror(@error_message, 16,1)
    print 'Result is: '+ isnull(@res2, 'empty')
end catch

私たちは(そして私は動揺しています):

メッセージ50000、レベル16、状態1、行28
これはエラーです
結果は空です

エラーメッセージは問題ありませんが、出力パラメーターは[〜#〜] null [〜#〜]になりました。場合、TRY...CATCHコンテキスト、実行はRAISERRORの直後に停止します。出力値がhelloに設定されることを期待していました。

なぜそうなのですか?

7
irimias

そのテストセットアップは順調にスタートしましたが、実際に何が起こっているのかを誤解させる原因となる何かが欠けています。ストアドプロシージャの最初、中間、および最後にPRINTステートメントを入力すると、追加の出力により、ここで何が行われているのかが明確になります。例えば:

USE [tempdb];
GO
CREATE PROCEDURE test_output
(
  @Result VARCHAR(10) OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;

    SET @Result = 'hello';

    PRINT 1;
    RAISERROR('This is an error', 16, 1);

    PRINT 2;
    SET @Result = 'error';

    PRINT 3;
END;
GO

最初のテストクエリの出力は次のとおりです。

1
Msg 50000, Level 16, State 1, Procedure test_output, Line xxxx [Batch Start Line yyyyy]
This is an error
2
3
Result is: error

そして、それはおそらくあなたがとにかく期待していたことです。しかし、2番目のテストクエリの出力は次のとおりです。

1
Msg 50000, Level 16, State 1, Line xxxxx
This is an error
Result is: empty

それはかなり異なります。 TRY...CATCH構文内で、RAISERRORが呼び出されると(つまり、バッチ中止イベントになると)実行が停止されていることがわかります即時。一方、RAISERRORは、TRY...CATCH構文内で呼び出されない場合、実行をすぐに停止しません。

ただし、質問の更新で指摘したように、OUTPUTパラメータがhelloに設定されない理由はこれで説明されません。これは、通常のストアドプロシージャの動作がnotに意図されているためです(バッチ中止エラーのため)部分的な実行を反映します。これについては、次のブログ投稿で説明されています。

TSQLの基礎II –セマンティクスを渡すパラメーター

意味:ストアドプロシージャが変数をhelloに設定するステップを実行した場合でも、TRY...CATCH構造内で呼び出されたRAISERRORはバッチ中止エラーとなり、ストアドプロシージャパラメータが中止された場合、パラメータへの変更は反映されません。

この動作は、次の説明の中心にもあります。

TVPを読み取り専用にする必要がある理由、および他のタイプのパラメーターを読み取り専用にできない理由

5
Solomon Rutzky