PL / pgSQLでクエリが結果を返さないかどうかを確認する簡単な方法はありますか?


16

私は現在PL / pgSQLを少し実験していますが、次のようなもっとエレガントな方法があるかどうか知りたいです:

select c.data into data from doc c where c.doc_id = id and c.group_cur > group_cur order by c.id desc limit 1;
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        select c.data into data from doc c where c.doc_id = id and c.global_cur > global_cur order by c.id desc limit 1;
        EXCEPTION
            WHEN NO_DATA_FOUND THEN
                RETURN NULL;

回答:


21

例外ブロックは、条件をチェックするのではなく、エラーをトラップするためのものです。言い換えると、コンパイル時に何らかの条件を処理できる場合、エラーとしてトラップされるのではなく、通常のプログラムロジックによって解決される必要があります。

にPL / pgSQLのドキュメントのエラーのセクションをトラップあなたは、このようなヒントを見つけることができます。

ヒント:EXCEPTION句を含むブロックは、ブロックを持たないブロックよりも出入りするのに非常にコストがかかります。したがって、必要なくEXCEPTIONを使用しないでください。

代わりに、例外(悪い)、またはIF / THEN / ELSIF(良い)を使用して、これを1つのクエリに書き換えることができます。

SELECT c.data into data
FROM  doc c
WHERE c.doc_id = id
  and (
    c.group_cur > group_cur
    or
    c.global_cur > global_cur
  )
ORDER BY
  -- this will make group always preferred over global
  case when c.group_cur > group_cur then 1 else 2 end ASC,
  -- and this is your normal ordering
  c.id DESC
limit 1;

本当に2つのクエリが必要な場合は、特別なFOUND変数を使用して、以前のクエリで結果が得られたかどうかをテストできます。

select c.data into data
from doc c
where c.doc_id = id and c.group_cur > group_cur
order by c.id desc limit 1;
if not found then
    select c.data into data
    from doc c
    where c.doc_id = id and c.global_cur > global_cur
    order by c.id desc limit 1;
    if not found then return null; end if;
end if;

必須のRTFMリンクは次のとおりです:-)

変数の説明についてはこれを、/ ブロックについてはこれを参照してください。FOUNDIFTHEN


13

ブール型の特別な変数FOUNDを調べることができます。ドキュメントから:

FOUNDは、各PL / pgSQL関数呼び出し内でfalseから始まります。次の各タイプのステートメントによって設定されます。

SELECT INTOステートメントは、行が割り当てられている場合はFOUNDをtrueに設定し、行が返されない場合はfalseを設定します。

PERFORMステートメントは、1つ以上の行を生成(および破棄)する場合はFOUNDをtrueに設定し、行が生成されない場合はfalseを設定します。

UPDATE、INSERT、およびDELETEステートメントは、少なくとも1つの行が影響を受ける場合はFOUNDをtrueに設定し、行が影響を受けない場合はfalseを設定します。

FETCHステートメントは、行を返す場合はFOUNDをtrueに設定し、行が返されない場合はfalseを設定します。

MOVEステートメントは、カーソルを正常に再配置する場合はFOUNDをtrueに設定し、そうでない場合はfalseを設定します。

FORまたはFOREACHステートメントは、1回以上反復する場合はFOUNDをtrueに設定し、そうでない場合はfalseに設定します。FOUNDは、ループの終了時にこのように設定されます。ループの実行中、FOUNDはループステートメントによって変更されませんが、ループ本体内の他のステートメントの実行によって変更される場合があります。

RETURN QUERYおよびRETURN QUERY EXECUTEステートメントは、クエリが少なくとも1行を返す場合はFOUNDを設定し、行が返されない場合はfalseを設定します。

他のPL / pgSQLステートメントは、FOUNDの状態を変更しません。特に、EXECUTEはGET DIAGNOSTICSの出力を変更しますが、FOUNDは変更しません。

FOUNDは、各PL / pgSQL関数内のローカル変数です。変更すると、現在の機能のみに影響します。


しかし、select intoデータを返さない場合でも例外が発生しますよね?
ジャックダグラス

3
一般的にいいえ、SELECT * INTO STRICT my record ...のようにSTRICT句が指定されている場合にのみ例外を発生させます。
alexk

ああ、私の悪い-OPの例の例外ハンドラーが起動しないということではないのですか?:
ジャックダグラス

1
@JackDouglas:通常、データは例外の原因ではありません(上記のSTRICT修飾子のような特別な場合を除く)。OPには誤解がありました。
アーウィンブランドステッター
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.