web-dev-qa-db-ja.com

配列の引数値を使用したPostgresQL動的実行

これがPostgresで可能かどうか疑問に思っています:

不自然な例を使用して最もよく説明されます:

create or replace function test_function(filter_param1 varchar default null
                                       , filter_param2 varchar default null)
  returns integer as
$$ 
declare
  stmt text;
  args varchar[];
  wher varchar[];
  retid integer;
begin
  if filter_param1 is not null then 
    array_append(args, filter_param1);
    array_append(wher, 'parameter_name = $1');
  end if;
  if filter_param2 is not null then 
    array_append(args, filter_param2);
    array_append(wher, 'parameter_name = $2');
  end if;

  stmt := 'select id from mytable where ' || array_to_string(wher, ' or ');
  execute stmt into retid using args;

  return retid;
end;
$$ language plpgsql;

Pythonには*args-おそらくPostgreSQLにも同様のメカニズムがありますか?

Erwin Brandstetterの質問の編集:

  • すべてのfilterパラメータは異なる列に適用されますが、ANDする必要があります。
  • setofを返すことは、ここではずっと意味があります。
  • すべてのパラメーターは同じ列タイプ(つまり、varchar)にすることができます。
7
Richard

どちらの方法でも、すべてのパラメーターが同じデータ型であるとすれば、それは完全に可能です。

EXECUTE ... USING 喜んで配列を取り、これはsingle引数として扱われます。配列の添え字を持つ要素にアクセスします。

create or replace function test_function(_filter1 text = null
                                       , _filter2 text = null
                                       , OUT retid int) as
$func$
declare
   _args text[] := ARRAY[_filter1, _filter2];
   _wher text[];
begin
   if _filter1 is not null then 
      _wher := _wher || 'parameter_name = $1[1]'; -- note array subscript
   end if;

   if _filter2 is not null then 
      _wher := _wher || 'parameter_name = $1[2]'; -- assign the result!
   end if;

   IF _args  IS NULL         -- check whether all params are NULL
      RAISE EXCEPTION 'At least one parameter required!';
   END IF;

   execute 'select id from mytable where ' -- cover case with all params NULL
         || array_to_string(_wher, ' or ')
         || ' ORDER BY id LIMIT 1';   -- For a single value (???)
   into  retid
   using _args;
end
$func$  language plpgsql;

これは単なる概念実証であり、不必要に複雑です。これは、たとえば VARIADIC function を使用した、 actual 配列入力の興味深いオプションになります。

目下のケースでは、代わりに使用:

CREATE OR REPLACE FUNCTION test_function(_filter1 text = null
                                       , _filter2 text = null)
  RETURNS SETOF int AS
$func$
DECLARE
   _wher text := concat_ws(' OR '
             , CASE WHEN _filter1 IS NOT NULL THEN 'parameter_name = $1' END
             , CASE WHEN _filter2 IS NOT NULL THEN 'parameter_name = $2' END);
BEGIN
   IF _wher = ''   -- check whether all params are NULL
      RAISE EXCEPTION 'At least one parameter required!';
   END IF;

   RETURN QUERY EXECUTE 'SELECT id FROM mytable WHERE ' || _wher
   USING  $1, $2;
   -- USING  _filter1 , filter2; -- alternatively use func param names
END
$func$  LANGUAGE plpgsql;

説明する

  • 動的クエリで参照される可能性のあるすべての値をUSING句の出現順にリストします。それらのすべてが動的クエリで参照されない場合でも、害はありません。ただし、通常の位置をそのまま維持する必要があります。

  • 特に、動的クエリ内の$nは、USING句の指定された値を序数で参照しますが、-$nUSINGは、関数パラメーターを参照します。同じ構文、異なるスコープ!
    私の例では、単純化のために$2$2を参照しています。ただし、USING句の値を任意の方法で並べ替えることができるため、(たとえば)動的クエリの$2は、USING句の2番目の位置にある$1を参照します。パラメータ。

  • これにより、 any any (heterogeneous)data typesのパラメータ数が可能になります。

  • この例では整数のセットを返します(RETURNS SETOF int)。これは例に適しています。それに応じて RETURN QUERY EXECUTE を使用します。

  • concat_ws() は、条件付きでORまたはANDされた述語のリストを組み立てるのに特に便利です。

5