In a basic Postgres function tutorial there is an example with OUT
parameters like so:
create or replace function hi_lo(a numeric,
In Postgres 9.3 or later this is best solved with a LATERAL
join:
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;
Avoids repeated evaluation of the function (for each column in the output - the function does have to be called for each input row either way).
LEFT JOIN LATERAL ... ON true
to avoid dropping rows from the left side if the function returns no row:
Follow-up in 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;
But since you don't care about other columns, you can simplify to:
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;
Which is an implicit CROSS JOIN LATERAL
. If the function can actually return "no row" occasionally, the result can be different: we don't get NULL values for the rows, those rows are just eliminated - and LIMIT
does not count them any more.
In older versions (or generally) you can also just decompose the composite type with the right syntax:
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;
The drawback is that the function is evaluated once for each column in the function output due to a weakness in the Postgres query planner. It's better to move the call into a subquery or CTE and decompose the row type in the outer SELECT
. Like:
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;
But you have to name individual columns and can't get away with SELECT *
unless you are ok with the row type in the result redundantly.
Related: