In Postgres 9.3 oder später wird dies am besten mit einem LATERAL
gelöst beitreten:
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;
Vermeidet die wiederholte Auswertung der Funktion (für jede Spalte in der Ausgabe - die Funktion muss so oder so für jede Eingabezeile aufgerufen werden).LEFT JOIN LATERAL ... ON true
Um zu vermeiden, dass Zeilen von der linken Seite gelöscht werden, wenn die Funktion keine Zeile zurückgibt:
- Was ist der Unterschied zwischen LATERAL und einer Unterabfrage in PostgreSQL?
Follow-up in Ihrem Kommentar:
nur die durch den Funktionsaufruf erzeugten expandierten Spalten
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;
Aber da Sie sich nicht um andere Spalten kümmern, können Sie Folgendes vereinfachen:
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;
Das ist ein impliziter CROSS JOIN LATERAL
. Wenn die Funktion tatsächlich gelegentlich "keine Zeile" zurückgeben kann, kann das Ergebnis anders sein:Wir erhalten keine NULL-Werte für die Zeilen, diese Zeilen werden einfach eliminiert - und LIMIT
zählt sie nicht mehr.
In älteren Versionen (oder allgemein) Sie können den zusammengesetzten Typ auch einfach mit der richtigen Syntax zerlegen:
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;
Der Nachteil ist, dass die Funktion aufgrund einer Schwachstelle im Postgres-Abfrageplaner einmal für jede Spalte in der Funktionsausgabe ausgewertet wird. Es ist besser, den Aufruf in eine Unterabfrage oder CTE zu verschieben und den Zeilentyp im äußeren SELECT
zu zerlegen . Wie:
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;
Aber Sie müssen einzelne Spalten benennen und kommen nicht mit SELECT *
davon es sei denn, Sie sind mit dem Zeilentyp im Ergebnis redundant einverstanden.Verwandte:
- Vermeiden Sie mehrere Aufrufe derselben Funktion, wenn Sie das zusammengesetzte Ergebnis erweitern
- Wie kann man mehrere Funktionsauswertungen mit der (func()).*-Syntax in einer SQL-Abfrage vermeiden?