【发布时间】:2014-11-25 07:29:22
【问题描述】:
概述
我正在使用 PostgreSQL 9.1.14,我正在尝试将一个函数的结果传递给另一个函数。一般的想法(具体的,下面是一个最小的例子)是我们可以这样写:
select * from (select * from foo ...)
我们可以在一个函数中抽象出子选择并从中选择:
create function foos()
returns setof foo
language sql as $$
select * from foo ...
$$;
select * from foos()
有没有办法进一步抽象一层,以便能够做这样的事情(我知道函数实际上不能有 setof 类型的参数):
create function more_foos( some_foos setof foo )
language sql as $$
select * from some_foos ... -- or unnest(some_foos), or ???
$$:
select * from more_foos(foos())
最小示例和尝试的解决方法
我使用的是 PostgreSQL 9.1.14。这是一个最小的例子:
-- 1. create a table x with three rows
drop table if exists x cascade;
create table if not exists x (id int, name text);
insert into x values (1,'a'), (2,'b'), (3,'c');
-- 2. xs() is a function with type `setof x`
create or replace function xs()
returns setof x
language sql as $$
select * from x
$$;
-- 3. xxs() should return the context of x, too
-- Ideally the argument would be a `setof x`,
-- but that's not allowed (see below).
create or replace function xxs(x[])
returns setof x
language sql as $$
select x.* from x
join unnest($1) y
on x.id = y.id
$$;
当我加载这段代码时,我得到了表定义的预期输出,我可以像我期望的那样从xs() 中调用和选择。但是当我尝试将xs() 的结果传递给xxs() 时,出现“函数xxs(x) 不存在”的错误:
db=> \i test.sql
DROP TABLE
CREATE TABLE
INSERT 0 3
CREATE FUNCTION
CREATE FUNCTION
db=> select * from xs();
1 | a
2 | b
3 | c
db=> select * from xxs(xs());
ERROR: function xxs(x) does not exist
LINE 1: select * from xxs(xs());
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
我对“函数 xxs(x) 不存在”有点困惑;因为xs() 的返回类型是setof x,我希望它的返回类型是setof x(或者可能是x[]),而不是x。在收到关于该类型的投诉之后,我可以找到以下任何一个,但无论使用哪种定义,我都可以select xxs(xs());,但不能select * from xxs(xs());。
create or replace function xxs( x )
returns setof x
language sql as $$
select x.* from x
join unnest(array[$1]) y -- unnest(array[...]) seems pretty bad
on x.id = y.id
$$;
create or replace function xxs( x )
returns setof x
language sql as $$
select * from x
where x.id in ($1.id)
$$;
db=> select xxs(xs());
(1,a)
(2,b)
(3,c)
db=> select * from xxs(xs());
ERROR: set-valued function called in context that cannot accept a set
总结
将返回集合的函数的结果传递给另一个函数的正确方法是什么? (我注意到 create function ... xxs( setof x ) ... 会导致错误:ERROR: functions cannot accept set arguments,因此答案不会真正通过从一个函数到另一个函数的一组行。)
【问题讨论】:
-
将返回集合的函数的结果传递给另一个函数的正确方法是什么?:没有,因为函数不接受结果集作为输入。可能导致不这样认为的是
select xxs(xs())工作,但这是一个特例,一个黑客。它实际上调用xs()一次,遍历行,为每一行调用xss(x)。如果要传递结果集,请使用游标。 -
@DanielVérité 我开始担心这种情况。我们可以:(i)从子查询中选择,例如
select * from (select ...),这对我来说似乎很奇怪; (ii) 编写从查询返回结果集的函数,例如create function foo() ... select * from ...; (iii) 从此类函数的返回值中进行选择,例如,select * from foo();但不能 (iv) 再抽象出一个层次并做一些create function bar(X) ... select from X的变体。 -
@DanielVérité 同时,我将研究游标,但这似乎是一个遗漏 [说该语言的新手 ;)] 因为在大多数语言中,如果您可以将表达式替换为调用以表达式为主体的函数时,您还希望能够将函数调用替换为保存先前调用该函数的结果的变量。但正如我所说,我在这里有点新人,所以我没有任何接近其他人的观点;可能有一些我还没有看到的很好的理由。
-
您收到“function xxs(x) doesn't exist”的原因是因为数据库倾向于支持函数重载,在这种情况下,您可以拥有多个函数名称相同但每个函数名称不同的函数参数集。因此,在运行时,当它尝试查找您的函数时,它会同时使用函数名称和参数来查找函数的正确副本。函数 xxs(x) 显然存在,但不是一个接受结果集作为输入的函数。
-
@MikeJones
xxs(x)中的x是参数的类型(因为x是表的名称)。奇怪的是,虽然xs()返回一个setof x,但系统正在寻找一个接受单个x的函数。正如上面 DanielVerite 解释的那样,其原因是 xxs(xs()) 实际上是在调用 xs(),取回结果(一组 x)并尝试在每个结果上调用 xxs(...),这意味着需要 xxs(x)。
标签: sql postgresql