PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Den von der Funktion zurückgegebenen Datensatz in mehrere Spalten aufteilen

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?