1

PostgreSQLにストアドプロシージャがあります

CREATE OR REPLACE FUNCTION show_senti_lang_setting(IN _senti_id bigint)
RETURNS TABLE(lang_code character, native_name character varying, is_active boolean) AS
$BODY$
BEGIN
RETURN QUERY 
    SELECT
        l.lang_code,
        l.native_name,
        (CASE WHEN s.senti_id is NULL THEN FALSE
            ELSE TRUE
        END) is_active
    FROM
        language l
    LEFT JOIN senti_lang s
    ON s.lang_code=l.lang_code
    AND s.senti_id=_senti_id;
END;
$BODY$
LANGUAGE plpgsql VOLATILE STRICT;

エラーは次のとおりです。

ERROR:  syntax error at or near "$1"
LINE 1: ...HEN s.senti_id is NULL THEN FALSE ELSE TRUE END)  $1  FROM l...
                                                         ^
QUERY:   SELECT l.lang_code, l.native_name, (CASE WHEN s.senti_id is NULL THEN FALSE ELSE TRUE END)  $1  FROM language l LEFT JOIN senti_lang s ON s.lang_code=l.lang_code AND s.senti_id= $2 
CONTEXT:  SQL statement in PL/PgSQL function "show_senti_lang_setting" near line 13

********** Error **********

ERROR: syntax error at or near "$1"
SQL state: 42601
Context: SQL statement in PL/PgSQL function "show_senti_lang_setting" near line 13

エラーはCASEplpgsqlが原因のようです。の同じ関数がSQLでうまく機能しています。

CREATE OR REPLACE FUNCTION show_senti_lang_setting(bigint)
    RETURNS TABLE(lang_code character, native_name character varying, is_active boolean) AS
$BODY$

    SELECT
        l.lang_code,
        l.native_name,
        CASE WHEN s.senti_id is NULL THEN FALSE
            ELSE TRUE
        END is_active
    FROM
        language l
    LEFT JOIN senti_lang s
    ON s.lang_code=l.lang_code
    AND s.senti_id=$1;

$BODY$
  LANGUAGE sql VOLATILE STRICT;
4

2 に答える 2

6

あなたが持っているものに関して:

バージョン8.4OUTには、返された行()の列エイリアスと同じ名前のパラメーターに問題がありましたis_active。これは現在修正されており、PostgreSQL 9.1以降(おそらく9.0でも)で動作します。それが構文エラーの理由です。

このコンステレーションでは、列のエイリアスは単なるノイズです。それらは、宣言されたOUTパラメーターを優先して破棄されます。それらの唯一の目的はドキュメントである可能性があるため、コメントにして、最初から競合を回避してください。

    CASE WHEN s.senti_id is NULL THEN FALSE ELSE TRUE END -- AS is_active

また:

  • キーワードASは列エイリアスとともに使用する必要があります(テーブルエイリアスの場合はスキップしてもかまいません)。
  • CASEステートメントの前後に括弧は必要ありません。

より良い形

あなたがそれを持っている方法では、あなたは常にテーブルからすべての行を返します-そして一致するものが見つかっlanguageた1つ以上のインスタンス。senti_lang同時に、関数を定義するSTRICTため、にNULL値を指定しても、行はまったく取得されません_senti_id。そのための賢明なユースケースを想像するのは非常に困難です。

で複数の一致が見つかった場合、言語ごとに複数の行を返したいとは思わないsenti_lang。したがって、次のように簡略化できます。

CREATE OR REPLACE FUNCTION show_senti_lang_setting(IN _senti_id bigint)
RETURNS TABLE(lang_code character, native_name varchar, is_active boolean) AS
$func$
BEGIN
RETURN QUERY 
   SELECT l.lang_code
         ,l.native_name
         ,EXISTS (SELECT 1 FROM senti_lang s
                  WHERE  s.lang_code = l.lang_code
                  AND    s.senti_id = _senti_id) -- AS is_active
   FROM   language l;
END
$func$ LANGUAGE plpgsql VOLATILE STRICT;

そして、私はそれが必要かどうかという質問を提起しますSTRICT

の意味Select 1

以下のコメントのフォローアップ質問への返信。私はマニュアルEXISTSを引用します:

結果は行が返されるかどうかにのみ依存し、それらの行の内容には依存しないため、サブクエリの出力リストは通常​​重要ではありません。一般的なコーディング規則は、すべてのEXISTSテストをEXISTS(SELECT 1 WHERE ...)の形式で記述することです。

基本的に、構文的に有効な式を記述できます。とにかく捨てられます。この関連する質問
の下で読みやすさについて議論してきました。

于 2013-01-05T14:58:01.723 に答える
1

これは、パラメータとの列エイリアス間の名前の衝突のようcaseです。

選択クエリでエイリアスの名前を変更すると、機能するはずです

SELECT
    l.lang_code,
    l.native_name,
    (CASE WHEN s.senti_id is NULL THEN FALSE
        ELSE TRUE
    END) as active_flag  -- <### this is the change 
FROM
    language l
LEFT JOIN senti_lang s
ON s.lang_code=l.lang_code
AND s.senti_id=_senti_id;

SQLFiddleの例:http ://sqlfiddle.com/#!11/49075/1

ケース式の値は、位置によって「出力」パラメーターにマップされます。したがって、それらは異なる名前を持つことができます(そして明らかにそうしなければなりません)。

于 2013-01-05T13:48:33.923 に答える