web-dev-qa-db-ja.com

ストアドプロシージャのパラメーター検証を行う「正しい」方法

いくつかのパラメーターの検証を行うストアドプロシージャがあり、パラメーターが無効な場合、失敗して実行を停止する必要があります。

エラーチェックの最初の方法は次のとおりです。

create proc spBaz
(
  @fooInt int = 0,
  @fooString varchar(10) = null,
  @barInt int = 0,
  @barString varchar(10) = null
)
as
begin
  if (@fooInt = 0 and (@fooString is null or @fooString = ''))
    raiserror('invalid parameter: foo', 18, 0)

  if (@barInt = 0 and (@barString is null or @barString = ''))
    raiserror('invalid parameter: bar', 18, 0)

  print 'validation succeeded'
  -- do some work
end

重大度18は実行を停止せず、エラーメッセージと共に「検証成功」が出力されるため、これはうまくいきませんでした。

すべてのレイサーの後に単にリターンを追加できることは知っていますが、これは私にはちょっと醜いように見えます:

  if (@fooInt = 0 and (@fooString is null or @fooString = ''))
  begin
    raiserror('invalid parameter: foo', 18, 0)
    return
  end

  ...

  print 'validation succeeded'
  -- do some work

重大度が11以上のエラーはtry/catchブロック内でキャッチされるため、テストした別のアプローチは、そのようなtry/catchブロック内にエラーチェックをカプセル化することでした。問題は、エラーが飲み込まれ、クライアントにまったく送信されないことでした。だから私はいくつかの研究をして、 rethrow エラーへの方法を見つけました:

  begin try
    if (@fooInt = 0 and (@fooString is null or @fooString = ''))
      raiserror('invalid parameter: foo', 18, 0)

    ...
  end try
  begin catch
    exec usp_RethrowError
    return
  end catch

  print 'validation succeeded'
  -- do some work

私はまだこのアプローチに満足していないので、私はあなたに尋ねています:

パラメーター検証はどのように見えますか?この種のチェックを行うための「ベストプラクティス」はありますか?

34
VVS

これを行うための単一の「正しい」方法があるとは思いません。

私自身の設定は2番目の例に似ていますが、各パラメーターの個別の検証ステップと、より明示的なエラーメッセージが含まれます。

あなたが言うように、それは少し面倒で醜いですが、コードの意図はそれを読んでいる誰にとっても明白であり、それは仕事を成し遂げます。

IF (ISNULL(@fooInt, 0) = 0)
BEGIN
    RAISERROR('Invalid parameter: @fooInt cannot be NULL or zero', 18, 0)
    RETURN
END

IF (ISNULL(@fooString, '') = '')
BEGIN
    RAISERROR('Invalid parameter: @fooString cannot be NULL or empty', 18, 0)
    RETURN
END
48
LukeH

この回答の履歴からわかるように、私はこの質問に従い、回答を受け入れ、基本的に2番目のアプローチと同じソリューションを「発明」しました。

コーディングにあまりにも多くの時間を費やしているため、人生のほとんどを半眠っているという事実により、カフェインは私の主なエネルギー源です。したがって、あなたが正しく指摘するまで、私は私の偽物に気づきませんでした。

したがって、記録のために、私はあなたの2番目のアプローチを好む:SPを使用して現在のエラーを発生させ、次にパラメーターの検証にTRY/CATCHを使用する。

すべてのIF/BEGIN/ENDブロックの必要性が減少するため、行数が減少し、検証に焦点が戻ります。 SPのコードを読むとき、パラメーターに対して実行されているテストを確認できることが重要です。SQLパーサーを満たすためのすべての余分な構文のフラフが邪魔になります。意見。

1
Andras Zoltan

通常、raiseerror()は回避し、エラーを示す値(負の数など)を返します。

if <errorcondition>
    return -1

または、2つの出力パラメーターで結果を渡します。

create procedure dbo.TestProc
    ....
    @result int output,
    @errormessage varchar(256) output
as
set @result = -99
set @errormessage = null
....
if <errorcondition>
    begin
    set @result = -1
    set @errormessage = 'Condition failed'
    return @result
    end
1
Andomar

私は常に出力としてパラメーター@Is_Successビットを使用します。したがって、エラーが発生した場合は、@ Is_success = 0になります。親プロシージャが@ Is_Success = 0であることを確認すると、トランザクション(子トランザクションを含む)がロールバックされ、@ Error_Messageからクライアントにエラーメッセージが送信されます。

0
Dalex

私はできるだけ早く帰宅することを好んでおり、手順の最後にすべてが同じ地点から帰ってくるのは意味がないと思います。数年前、私はこの習慣を身につけました。また、私は常に値を返します:

RETURN 10

アプリケーションは、正の数で致命的なエラーを表示し、負の値でユーザー警告メッセージを表示します。

エラーメッセージのテキストを含むOUTPUTパラメータを常に返します。

例:

IF ~error~
BEGIN
    --if it is possible to be within a transaction, so any error logging is not ROLLBACK later
    IF XACT_STATE()!=0
    BEGIN
        ROLLBACK
    END

    SET @OutputErrMsg='your message here!!'
    INSERT INTO ErrorLog (....) VALUES (.... @OutputErrMsg)
    RETURN 10

END
0
KM.