在基本的 Postgres 函数教程中有一个带有 OUT
参数的示例,如下所示:
create or replace function hi_lo(a numeric,
b numeric,
c numeric,
OUT hi numeric,
OUT lo numeric)
as $$
begin
hi := greatest(a, b, c);
lo := least(a, b, c);
end; $$
language plpgsql;
然后结果看起来像
select hi_lo(2, 3, 4);
-- returns one column, "hi_lo" with value "(4, 2)".
select * from hi_lo(2, 3, 4);
-- returns two columns, "hi" / 4 and "lo" / 2.
但是假设您想在执行连接后产生的列上执行该函数,并且您无权修改该函数或使用替代函数?例如,使用一些玩具数据:
select hi_lo(a.actor_id, length(a.name), ma.movie_id)
from
actors a
join
movies_actors ma
on
a.actor_id = ma.movie_id
limit 10;
返回单列“hi_lo”中的结果具有 2 元组值。
将查询包裹在括号中并尝试从中选择 select *
不会更改输出格式。所以
select *
from (
select hi_lo(a.actor_id, length(a.name), ma.movie_id)
from
actors a
join
movies_actors ma
on
a.actor_id = ma.movie_id
limit 10;
) rr
不影响结果形状。
下面的尝试导致错误“subquery must return only one column”
select (
select * from hi_lo(a.actor_id, length(a.name), ma.movie_id)
)
from
actors a
join
movies_actors ma
on
a.actor_id = ma.movie_id
limit 10;
最后,我还尝试了 unnest
但它给出了参数类型错误,因为元组值未被视为数组。
当无法将函数求值移动到 from
部分时,如何在输出中实现多列?
最佳答案
Postgres 9.3 或更高版本
最好用 LATERAL
连接解决:
SELECT *
FROM actors a
JOIN movies_actors ma on a.actor_id = ma.movie_id
LEFT JOIN LATERAL hi_lo(a.actor_id, length(a.name), ma.movie_id) x ON true
LIMIT 10;
避免函数的重复计算(对于输出中的每一列 - 无论哪种方式都必须为每个输入行调用该函数)。见:
LEFT JOIN LATERAL ... ON true
以避免在右侧的函数未返回任何行时从左侧删除行。见:
寻址 your comment :
only the expanded columns produced by the function call
SELECT x.* -- that's all!
FROM actors a
JOIN movies_actors ma on a.actor_id = ma.movie_id
LEFT JOIN LATERAL hi_lo(a.actor_id, length(a.name), ma.movie_id) x ON true
LIMIT 10;
但由于您不关心其他列,您可以简化为:
SELECT x.*
FROM actors a
JOIN movies_actors ma on a.actor_id = ma.movie_id
, hi_lo(a.actor_id, length(a.name), ma.movie_id) x
LIMIT 10;
这是一个隐式的CROSS JOIN LATERAL
。如果该函数实际上可以偶尔返回“无行”,结果可能会有所不同:我们没有得到行的 NULL 值,这些行只是被消除了 - LIMIT
不再对它们进行计数.
旧版本(或一般)
您也可以使用正确的语法分解复合类型:
SELECT *, <b>(</b>hi_lo(a.actor_id, length(a.name), ma.movie_id)<b>).*</b> -- note extra parentheses!
FROM actors a
JOIN movies_actors ma on a.actor_id = ma.movie_id
LIMIT 10;
缺点是由于顶部提到的 Postgres 查询规划器的弱点,函数输出中的每一列都会计算一次函数。最好将调用移动到子查询或 CTE 中,并在外部 SELECT
中分解行类型。喜欢:
SELECT actor_id, movie_id, (x).* -- explicit column names for the rest
FROM (
SELECT *, hi_lo(a.actor_id, length(a.name), ma.movie_id) AS x
FROM actors a
JOIN movies_actors ma on a.actor_id = ma.movie_id
LIMIT 10
) sub;
但是您必须为各个列命名并且不能使用 SELECT *
除非您对结果中的行类型感到满意。
相关:
关于sql - 将函数返回的记录拆分为多列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38297935/