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

Usa qualcosa come TOP con GROUP BY

Puoi comodamente recuperare il passeggero con il nome più lungo per gruppo con DISTINCT ON .

Ma non vedo alcun modo per combinare questo (o qualsiasi altro modo semplice) con la tua query originale in un unico SELECT . Suggerisco di unire due sottoquery separate:

SELECT *
FROM  (  -- your original query
   SELECT orig
        , count(*) AS flight_cnt
        , count(distinct passenger) AS pass_cnt
        , percentile_cont(0.5) WITHIN GROUP (ORDER BY bags) AS bag_cnt_med
   FROM   table1
   GROUP  BY orig
   ) org_query
JOIN  (  -- my addition
   SELECT DISTINCT ON (orig) orig, passenger AS pass_max_len_name
   FROM   table1
   ORDER  BY orig, length(passenger) DESC NULLS LAST
   ) pas USING (orig);

USING nella clausola join convenientemente restituisce solo un'istanza di orig , quindi puoi semplicemente usare SELECT * nel SELECT esterno .

Se passenger può essere NULL, è importante aggiungere NULLS LAST :

Da più nomi di passeggeri con la stessa lunghezza massima nello stesso gruppo, ottieni una scelta arbitraria - a meno che tu non aggiunga più espressioni a ORDER BY come spareggio. Spiegazione dettagliata nella risposta collegata sopra.

Prestazioni?

In genere, una singola scansione è superiore, specialmente con le scansioni sequenziali.

La query precedente ne utilizza due scansioni (forse scansioni indice/solo indice). Ma la seconda scansione è relativamente economica a meno che la tabella non sia troppo grande per stare nella cache (per lo più). Lukas ha suggerito una query alternativa con un solo singolo SELECT aggiungendo:

, (ARRAY_AGG (passenger ORDER BY LENGTH (passenger) DESC))[1]  -- I'd add NULLS LAST

L'idea è intelligente, ma l'ultima volta che ho provato , array_agg con ORDER BY non ha funzionato così bene. (Il sovraccarico di ORDER BY per gruppo è sostanziale e anche la gestione dell'array è costosa.)

Lo stesso approccio può essere più economico con una funzione aggregata personalizzata first() come indicato nel Wiki di Postgres qui . Oppure, ancora più veloce, con una versione scritta in C, disponibile su PGXN . Elimina il costo aggiuntivo per la gestione dell'array, ma abbiamo ancora bisogno di ORDER BY per gruppo . Potrebbe essere più veloce solo per pochi gruppi. Dovresti quindi aggiungere:

 , first(passenger ORDER BY length(passenger) DESC NULLS LAST)

Gordon e Lukas menziona anche la funzione finestra first_value() . Le funzioni della finestra vengono applicate dopo funzioni aggregate. Per usarlo nello stesso SELECT , dovremmo aggregare passenger in qualche modo first - catch 22. Gordon risolve questo problema con una sottoquery, un altro candidato per buone prestazioni con Postgres standard.

first() fa lo stesso senza subquery e dovrebbe essere più semplice e un po' più veloce. Ma non sarà comunque più veloce di un DISTINCT ON separato per la maggior parte dei casi con poche righe per gruppo. Per molte righe per gruppo, una tecnica CTE ricorsiva è in genere più veloce. Esistono tecniche ancora più veloci se si dispone di una tabella separata che contiene tutti gli orig pertinenti e univoci i valori. Dettagli:

La soluzione migliore dipende da vari fattori. La prova del budino è nel mangiarlo. Per ottimizzare le prestazioni devi testare con la tua configurazione. La query di cui sopra dovrebbe essere tra le più veloci.