In Postgres 9.3 o più tardi questo è meglio risolto con un LATERAL
unisciti:
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;
Evita la valutazione ripetuta della funzione (per ogni colonna nell'output - la funzione deve essere chiamata per ogni riga di input in entrambi i modi).LEFT JOIN LATERAL ... ON true
per evitare di far cadere le righe dal lato sinistro se la funzione non restituisce nessuna riga:
- Qual è la differenza tra LATERAL e una sottoquery in PostgreSQL?
Follow-up nel tuo commento:
solo le colonne espanse prodotte dalla chiamata di funzione
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;
Ma poiché non ti interessano altre colonne, puoi semplificare in:
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;
Che è un implicito CROSS JOIN LATERAL
. Se la funzione può effettivamente restituire "nessuna riga" occasionalmente, il risultato può essere diverso:non otteniamo valori NULL per le righe, quelle righe vengono semplicemente eliminate - e LIMIT
non li conta più.
Nelle versioni precedenti (o in generale) puoi anche semplicemente scomporre il tipo composito con la giusta sintassi:
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;
Lo svantaggio è che la funzione viene valutata una volta per ogni colonna nell'output della funzione a causa di una debolezza nel pianificatore di query di Postgres. È meglio spostare la chiamata in una sottoquery o CTE e scomporre il tipo di riga nel SELECT
esterno . Come:
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;
Ma devi nominare le singole colonne e non puoi farla franca con SELECT *
a meno che tu non sia d'accordo con il tipo di riga nel risultato in modo ridondante. Correlati:
- Evita più chiamate sulla stessa funzione quando espandi il risultato composito
- Come evitare più valutazioni di funzioni con la sintassi (func()).* in una query SQL?