PL/pgSQL 函数中的可选参数

Posted

技术标签:

【中文标题】PL/pgSQL 函数中的可选参数【英文标题】:Optional argument in PL/pgSQL function 【发布时间】:2012-07-16 04:36:39 【问题描述】:

我正在尝试编写一个带有可选参数的 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

【问题讨论】:

【参考方案1】:

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

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

由于您返回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$;

【讨论】:

+1 用于实际回答问题。这正是我想要的!【参考方案2】:

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

VARIADIC 参数不必是唯一的参数,只能是最后一个参数。

您可以将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[]);

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

【讨论】:

我想我可以简单地将函数签名更改为 CREATE OR REPLACE FUNCTION foofunc(int, date, date, VARIADIC ARRAY[]::integer[]) ?并用一个空列表的包装器重载函数(我认为这就是你所说的)。现在,关于传递一个 id 列表,我如何将一个 id 列表传递给 foofunc() - 请参阅我问题底部的旁边。 @HomunculusReticulli 问题已更新,以进一步明确和清晰。希望能帮助到你。我没有用语法检查把它们弄糊涂,我是在记忆中工作,但我最近在 Pg 中做了一些可变参数函数,所以它应该是正确的。 @HomunculusReticulli 该提议签名的后半部分无效;您不能将数组构造函数作为参数类型。省略ARRAY[]::,只接受variable_name VARIADIC integer[]【参考方案3】:

你的意思是SQL Functions with Variable Numbers of Arguments?如果是这样,请使用 VARIADIC。

【讨论】:

@HomunculusReticulli 请注意,VARIADIC 的行为与您在某些语言中可能使用的可变参数不同。特别是零可变参数不被接受。如果要使用零个可变参数进行调用,请使用语法the_varargs_function(fixedarg1, fixedarg2, VARIADIC ARRAY[]::argtype[]),其中argtype[] 是可变参数数组参数的类型。编写一个提供零参数形式的简单 LANGUAGE SQL 包装函数通常很方便。 不确定 VARIADIC 是否适用于所有场景。它唯一可能(或可能不)包含 id 列表的最后一个参数。之前的参数param1, param2 等将始终存在。 @CraigRinger:是的,这听起来更像是我想走的路。您能否充实您的评论作为答案(最好使用一点 SQL 代码),以便我可以从中学习?谢谢

以上是关于PL/pgSQL 函数中的可选参数的主要内容,如果未能解决你的问题,请参考以下文章

可变参数后元组中的可选函数参数

Python中的类/函数中的可选参数[重复]

Javascript:函数中的可选参数[重复]

javascript中的可选参数[重复]

Mathematica 中的可选命名参数

Java 构造函数 - 子类构造函数中的可选参数