在一个基本的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
不影响结果形状。
以下尝试导致错误“子查询必须只返回一列”
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
部分,如何在输出中实现多列?
答案 0 :(得分:4)
在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中的后续行动:
仅调用函数调用生成的扩展列
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
。如果函数实际上偶尔可以返回“no row”,结果可能会有所不同:我们没有得到行的NULL值,这些行只是被删除了 - LIMIT
不再计算它们。
在旧版本(或通常)中,您也可以使用正确的语法分解复合类型:
SELECT *, (hi_lo(a.actor_id, length(a.name), ma.movie_id)).* -- 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 *
,除非您对结果中的行类型进行冗余处理。
相关: