Du kan enkelt hämta passageraren med det längsta namnet per grupp med DISTINCT ON
.
Men jag ser inget sätt att kombinera det (eller något annat enkelt sätt) med din ursprungliga fråga i en enda SELECT
. Jag föreslår att du går med i två separata underfrågor:
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
i join-satsen matar lämpligen bara en instans av orig
, så du kan helt enkelt använda SELECT *
i den yttre SELECT
.
Om passenger
kan vara NULL, är det viktigt att lägga till NULLS LAST
:
Från flera passagerarnamn med samma maximala längd i samma grupp får du ett godtyckligt val - om du inte lägger till fler uttryck i ORDER BY
som tiebreaker. Detaljerad förklaring i svaret länkat ovan.
Prestanda?
Vanligtvis är en enda skanning överlägsen, särskilt med sekventiella skanningar.
Ovanstående fråga använder två skanningar (kanske index-/index-skanningar). Men den andra skanningen är jämförelsevis billig om inte bordet är för stort för att få plats i cachen (för det mesta). Lukas föreslog en alternativ fråga med endast en enkel SELECT
lägger till:
, (ARRAY_AGG (passenger ORDER BY LENGTH (passenger) DESC))[1] -- I'd add NULLS LAST
Idén är smart, men förra gången jag testade
, array_agg
med ORDER BY
presterade inte så bra. (Overheaden för ORDER BY
per grupp är betydande och arrayhantering är också dyr.)
Samma tillvägagångssätt kan vara billigare med en anpassad aggregatfunktion first()
gilla enligt instruktionerna i Postgres Wiki här
. Eller, snabbare, ändå, med en version skriven i C, tillgänglig på PGXN
. Eliminerar den extra kostnaden för arrayhantering, men vi behöver fortfarande ORDER BY
per grupp . Kan vara snabbare för endast ett fåtal grupper. Du skulle sedan lägga till:
, first(passenger ORDER BY length(passenger) DESC NULLS LAST)
Gordon
och Lukas
Nämn även fönsterfunktionen first_value()
. Fönsterfunktioner tillämpas efter aggregerade funktioner. För att använda den i samma SELECT
, skulle vi behöva samla passenger
på något sätt först - fånga 22. Gordon löser detta med en underfråga - en annan kandidat för bra prestanda med standard Postgres.
first()
gör samma sak utan underfråga och borde vara enklare och lite snabbare. Men det blir ändå inte snabbare än en separat DISTINCT ON
för de flesta fall med få rader per grupp. För många rader per grupp är en rekursiv CTE-teknik vanligtvis snabbare. Det finns ännu snabbare tekniker om du har en separat tabell som innehåller alla relevanta, unika orig
värden. Detaljer:
Den bästa lösningen beror på olika faktorer. Beviset på puddingen ligger i ätandet. För att optimera prestanda måste du testa med din installation. Ovanstående fråga bör vara bland de snabbaste.