web-dev-qa-db-ja.com

PostgreSQL-結果セットを返すストアドプロシージャに動的SQLを記述します

結果セットを返す動的に構築されたSQLステートメントを含むストアドプロシージャを作成するにはどうすればよいですか?これが私のサンプルコードです:

CREATE OR REPLACE FUNCTION reporting.report_get_countries_new (
  starts_with varchar,
  ends_with varchar
)
RETURNS TABLE (
  country_id integer,
  country_name varchar
) AS
$body$
DECLARE
  starts_with ALIAS FOR $1;
  ends_with ALIAS FOR $2;
  sql VARCHAR;
BEGIN

    sql = 'SELECT * FROM lookups.countries WHERE lookups.countries.country_name >= ' || starts_with ;

    IF ends_with IS NOT NULL THEN
        sql = sql || ' AND lookups.countries.country_name <= ' || ends_with ;
    END IF;

    RETURN QUERY EXECUTE sql;

END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
CALLED ON NULL INPUT
SECURITY INVOKER
COST 100 ROWS 1000;

このコードはエラーを返します:

ERROR:  syntax error at or near "RETURN"
LINE 1: RETURN QUERY SELECT * FROM omnipay_lookups.countries WHERE o...
        ^
QUERY:  RETURN QUERY SELECT * FROM omnipay_lookups.countries WHERE omnipay_lookups.countries.country_name >= r
CONTEXT:  PL/pgSQL function "report_get_countries_new" line 14 at EXECUTE statement

私はこれの代わりに他の方法を試しました:

RETURN QUERY EXECUTE sql;

方法1:

RETURN EXECUTE sql;

方法2:

sql = 'RETURN QUERY SELECT * FROM....
/*later*/
EXECUTE sql;

すべての場合で成功しませんでした。

最終的には、動的SQLステートメントを含み、動的SQLステートメントから結果セットを返すストアドプロシージャを作成したいと思います。

9
prince

改善の余地があります:

_CREATE OR REPLACE FUNCTION report_get_countries_new (starts_with text
                                                   , ends_with   text = NULL)
  RETURNS SETOF lookups.countries AS
$func$
DECLARE
   sql text := 'SELECT * FROM lookups.countries WHERE country_name >= $1';
BEGIN
   IF ends_with IS NOT NULL THEN
      sql := sql || ' AND country_name <= $2';
   END IF;

   RETURN QUERY EXECUTE sql
   USING starts_with, ends_with;
END
$func$ LANGUAGE plpgsql;
-- the rest is default settings
_

主なポイント

  • PostgreSQL 8.4では、USINGEXECUTE句が導入されました。これは、いくつかの理由で役立ちます。 マニュアルの要約

    コマンド文字列は、コマンドで_$1, $2_などとして参照されるパラメータ値を使用できます。これらの記号は、USING句で指定された値を参照します。この方法は、データ値をテキストとしてコマンド文字列に挿入するよりも望ましい場合がよくあります。値をテキストに変換したり元に戻したりする実行時のオーバーヘッドを回避し、引用符や引用符を使用する必要がないため、SQLインジェクション攻撃を受けにくくなります。脱出。

    IOW、quote_literal()でサニタイズした場合でも、パラメーターのテキスト表現を使用してクエリ文字列を作成するよりも安全で高速です。
    クエリ文字列の_$1, $2_は、USING句で指定された値を参照し、notは関数パラメータを参照することに注意してください。

  • _SELECT * FROM lookups.countries_を返す間、次のようにRETURN宣言を簡略化できます。

    _RETURNS SETOF lookups.countries
    _

    PostgreSQLには、すべてのテーブルに対して自動的に定義された複合タイプがあります。これを使って。その結果、関数はタイプによって異なり、テーブルを変更しようとするとエラーメッセージが表示されます。このような場合は、関数を削除して再作成してください。

    これは望ましい場合と望ましくない場合があります-一般的にはそうです!テーブルを変更する場合は、副作用を認識しておく必要があります。あなたがそれを持っている方法では、あなたの関数は静かに壊れて、次の呼び出しで例外を発生させます。

  • 示されているように、宣言の2番目のパラメーターに explicit default を指定すると、次のことができます(ただし、そうする必要はありません)。 )_ends_with_で上限を設定したくない場合は、呼び出しを単純化します。

    _SELECT * FROM report_get_countries_new('Zaire');
    _

    の代わりに:

    _SELECT * FROM report_get_countries_new('Zaire', NULL);
    _

    このコンテキストでは 関数のオーバーロード に注意してください。

  • (今のところ)許容できる場合でも、言語名_'plpgsql'_を引用しないでください。それは識別子です。

  • 宣言時に変数を割り当てることができます。余分なステップを節約します。

  • パラメータはヘッダーで指定されます。無意味な行を削除します。

    _ starts_with ALIAS FOR $1;
     ends_with ALIAS FOR $2;
    _
29