31

我正在尝试编写一个带有可选参数的 PL/pgSQL 函数。它基于过滤的记录集(如果指定)执行查询,否则对表中的整个数据集执行查询。

例如(伪代码)

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
4

3 回答 3

47

自 PostgreSQL 8.4(您似乎正在运行)以来,函数参数有默认值。如果你把你的参数放在最后并提供一个默认值,你可以简单地从调用中省略它:

CREATE OR REPLACE FUNCTION foofunc(_param1 integer
                                 , _param2 date
                                 , _ids    int[] DEFAULT '{}')
  RETURNS SETOF foobar         -- declare return type!
  LANGUAGE plpgsql AS
$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$;

要点:

  • 关键字DEFAULT用于声明参数默认值。简短的选择:=

  • param1我从凌乱的例子中删除了多余的部分。

  • 既然您 return 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[]);

您必须确保呼叫是明确的。如果你有另一个同名的函数和两个参数,Postgres 可能不知道选择哪个。显式转换(就像我演示的那样)缩小了范围。否则,无类型的字符串文字也可以工作,但显式永远不会受到伤害。

从另一个函数中调用:

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

   RETURN QUERY
   SELECT * FROM foofunc(_param1, _param2, _ids);
END
$func$;
于 2012-07-22T21:41:13.237 回答
4

详细阐述Frank在此线程上的回答:

agumentVARIADIC不必是唯一的参数,只有最后一个参数。

您可以使用VARIADIC可能采用零可变参数的函数,它只是有点复杂,因为它需要零参数的不同调用样式。您可以提供一个包装函数来隐藏丑陋。给定一个初始的 varardic 函数定义,例如:

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[]);

后两种形式适用于空数组。

于 2012-07-17T10:58:40.837 回答
1

您的意思是具有可变数量参数的 SQL 函数?如果是这样,请使用 VARIADIC。

于 2012-07-17T10:27:38.440 回答