PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Dividi il record restituito dalla funzione in più colonne

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?