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 函数中的可选参数的主要内容,如果未能解决你的问题,请参考以下文章