web-dev-qa-db-ja.com

PL / pgSQL関数のオプションの引数

オプションの引数を使用してPL/pgSQL関数を記述しようとしています。フィルター処理されたレコードセット(指定されている場合)に基づいてクエリを実行します。それ以外の場合は、テーブル内のデータセット全体に対してクエリを実行します。

たとえば(PSEUDO CODE)

CREATE OR REPLACE FUNCTION foofunc(param1 integer, param2 date, param2 date, optional_list_of_ids=[]) RETURNS SETOF RECORD AS $$
    IF len(optional_list_of_ids) > 0 THEN
        RETURN QUERY (SELECT * from foobar where f1=param1 AND f2=param2 AND id in optional_list_of_ids);
    ELSE
        RETURN QUERY (SELECT * from foobar where f1=param1 AND f2=param2);
    ENDIF
$$ LANGUAGE SQL;

この関数を実装する正しい方法は何でしょうか?

余談ですが、このような関数を別の外部関数で呼び出す方法を知りたいのですが。これは私がそれをする方法です-それは正しいですか、それとももっと良い方法がありますか?

CREATE FUNCTION foofuncwrapper(param1 integer, param2 date, param2 date) RETURNS SETOF RECORD AS $$
BEGIN
   CREATE TABLE ids AS SELECT id from foobar where id < 100;
   RETURN QUERY (SELECT * FROM foofunc(param1, param2, ids));
END
$$ LANGUAGE SQL
29

PostgreSQL 8.4(実行しているようです)以降、関数パラメーターのデフォルト値 があります 。パラメータを最後に配置してデフォルトを提供する場合は、呼び出しから単純に省略できます。

CREATE OR REPLACE FUNCTION foofunc(_param1 integer
                                 , _param2 date
                                 , _ids    int[] DEFAULT '{}')
  RETURNS SETOF foobar AS -- declare return type!
$func$
BEGIN  -- required for plpgsql
   IF _ids <> '{}'::int[] THEN -- exclude empty array and NULL
      RETURN QUERY
      SELECT *
      FROM   foobar
      WHERE  f1 = _param1
      AND    f2 = _param2
      AND    id = ANY(_ids); -- "IN" is not proper syntax for arrays
   ELSE
      RETURN QUERY
      SELECT *
      FROM   foobar
      WHERE  f1 = _param1
      AND    f2 = _param2;
   END IF;
END  -- required for plpgsql
$func$  LANGUAGE plpgsql;

主なポイント:

  • キーワードDEFAULTは、パラメーターのデフォルトを宣言するために使用されます。短い代替:=

  • 冗長なparam1を乱雑な例から削除しました。

  • SELECT * FROM foobarを返すため、戻り値の型をRETURNS SETOF foobarではなくRETURNS SETOF recordとして宣言します。匿名レコードを使用する後者の形式は非常に扱いにくく、すべての呼び出しで列定義リストを提供する必要があります。

  • 整数の配列(int[])を関数パラメーターとして使用しています。それに応じてIF式とWHERE句を適応させました。

  • IFステートメントはプレーンSQLでは使用できません。そのためにはLANGUAGE plpgsqlである必要があります。

_idsの有無にかかわらず呼び出します:

SELECT * FROM foofunc(1, '2012-1-1'::date);

実質的に同じ:

SELECT * FROM foofunc(1, '2012-1-1'::date, '{}'::int[]);

通話が明確であることを確認する必要があります。同じ名前と2つのパラメータを持つ別の関数がある場合、Postgresはどちらを選択するかわからない場合があります。明示的なキャスト(私が示すように)により、絞り込みます。それ以外の場合は、型指定されていない文字列リテラルも機能しますが、明示的であっても害はありません。

別の関数内から呼び出す:

CREATE FUNCTION foofuncwrapper(_param1 integer, _param2 date)
  RETURNS SETOF foobar AS
$func$
DECLARE
   _ids int[] := '{1,2,3}';
BEGIN
   -- irrelevant stuff

   RETURN QUERY
   SELECT * FROM foofunc(_param1, _param2, _ids);
END
$func$  LANGUAGE plgpsql;
42

このスレッドでの Frank の回答について詳しく説明します。

VARIADIC引数は唯一の引数である必要はなく、最後の引数のみである必要があります。

可変引数がゼロの関数にはVARIADICを使用できます。引数がゼロの場合は別の呼び出しスタイルが必要になるという点で少し面倒です。醜さを隠すラッパー関数を提供できます。次のような初期可変関数定義が与えられたとします。

CREATE OR REPLACE FUNCTION foofunc(param1 integer, param2 date, param2 date, optional_list_of_ids VARIADIC integer[]) RETURNS SETOF RECORD AS $$
....
$$ language sql;

引数がゼロの場合は、次のようなラッパーを使用します。

CREATE OR REPLACE FUNCTION foofunc(integer, date, date) RETURNS SETOF RECORD AS $body$
SELECT foofunc($1,$2,$3,VARIADIC ARRAY[]::integer[]);
$body$ LANGUAGE 'sql';

または、直接VARIADIC '{}'::integer[]のような空の配列でメイン関数を呼び出します。ラッパーは醜いですが、醜いので、ラッパーの使用をお勧めします。

直接呼び出しは可変長形式で行うことができます。

SELECT foofunc(1,'2011-01-01','2011-01-01', 1, 2, 3, 4);

...または配列ctorを含む配列呼び出しフォーム:

SELECT foofunc(1,'2011-01-01','2011-01-01', VARIADIC ARRAY[1,2,3,4]);

...または配列テキストリテラル形式:

SELECT foofunc(1,'2011-01-01','2011-01-01', VARIADIC '{1,2,3,4}'::int[]);

後者の2つの形式は、空の配列で機能します。

3
Craig Ringer

つまり、 可変数の引数を持つSQL関数 ?その場合は、VARIADICを使用してください。

1
Frank Heikens