ely*_*ely 6 sql postgresql record plpgsql
在基本的 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;
Run Code Online (Sandbox Code Playgroud)
然后结果看起来像
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.
Run Code Online (Sandbox Code Playgroud)
但是,假设您想要对执行联接的列执行该函数,并且您无权修改该函数或使用替代函数?例如,使用一些玩具数据:
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;
Run Code Online (Sandbox Code Playgroud)
在单列“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
Run Code Online (Sandbox Code Playgroud)
不影响结果形状。
以下尝试会导致错误“子查询必须仅返回一列”
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;
Run Code Online (Sandbox Code Playgroud)
最后,我也尝试过unnest,但它给出了参数类型错误,因为元组值不被视为数组。
当无法将函数求值移至该部分时,如何在输出中实现多列from?
最好通过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;
Run Code Online (Sandbox Code Playgroud)
避免重复计算函数(对于输出中的每一列 - 无论哪种方式都必须为每个输入行调用该函数)。看:
LEFT JOIN LATERAL ... ON true以避免在右侧的函数不返回行的情况下从左侧删除行。看:
针对您的评论:
仅由函数调用生成的扩展列
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;
Run Code Online (Sandbox Code Playgroud)
但由于您不关心其他列,因此可以简化为:
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;
Run Code Online (Sandbox Code Playgroud)
这是一个隐式的CROSS JOIN LATERAL。如果该函数实际上偶尔可以返回“无行”,则结果可能会有所不同:我们不会获得行的 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;Run Code Online (Sandbox Code Playgroud)
缺点是,由于顶部提到的 Postgres 查询规划器的弱点,函数输出中的每一列都会计算一次该函数。最好将调用移至子查询或 CTE 中,并分解外部中的行类型SELECT。喜欢:
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;Run Code Online (Sandbox Code Playgroud)
SELECT *但是您必须为各个列命名,除非您对结果中的行类型感到满意,否则无法逃脱。有关的: