sql >> Databasteknik >  >> RDS >> PostgreSQL

Använd något som TOP med GROUP BY

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.



  1. Ta bort från många till många relationer i MySQL

  2. mod rewrite, title slugs och htaccess

  3. SQL radbrytning fungerar inte

  4. Revision i Oracle