Dans Postgres 9.3 ou plus tard, cela est mieux résolu avec un LATERAL
rejoindre :
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;
Évite l'évaluation répétée de la fonction (pour chaque colonne de la sortie - la fonction doit être appelée pour chaque ligne d'entrée dans les deux sens).LEFT JOIN LATERAL ... ON true
pour éviter de supprimer des lignes du côté gauche si la fonction ne renvoie aucune ligne :
- Quelle est la différence entre LATERAL et une sous-requête dans PostgreSQL ?
Suite dans votre commentaire :
uniquement les colonnes développées produites par l'appel de la fonction
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;
Mais comme vous ne vous souciez pas des autres colonnes, vous pouvez simplifier :
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;
Qui est un CROSS JOIN LATERAL
implicite . Si la fonction peut réellement renvoyer "aucune ligne" de temps en temps, le résultat peut être différent :nous n'obtenons pas de valeurs NULL pour les lignes, ces lignes sont simplement éliminées - et LIMIT
ne les compte plus.
Dans les anciennes versions (ou généralement) vous pouvez aussi simplement décomposer le type composite avec la bonne syntaxe :
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;
L'inconvénient est que la fonction est évaluée une fois pour chaque colonne dans la sortie de la fonction en raison d'une faiblesse du planificateur de requêtes Postgres. Il est préférable de déplacer l'appel dans une sous-requête ou CTE et de décomposer le type de ligne dans le SELECT
externe . Comme :
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;
Mais vous devez nommer des colonnes individuelles et ne pouvez pas vous en sortir avec SELECT *
sauf si vous êtes d'accord avec le type de ligne dans le résultat de manière redondante.Related :
- Éviter plusieurs appels sur la même fonction lors de l'expansion du résultat composite
- Comment éviter plusieurs évaluations de fonction avec la syntaxe (func()).* dans une requête SQL ?