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

Brug noget som TOP med GROUP BY

Du kan nemt hente passageren med det længste navn pr. gruppe med DISTINCT ON .

Men jeg ser ingen måde at kombinere det (eller nogen anden simpel måde) med din oprindelige forespørgsel i en enkelt SELECT . Jeg foreslår at forbinde to separate underforespørgsler:

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-sætningen udsender bekvemt kun én forekomst af orig , så du kan blot bruge SELECT * i den ydre SELECT .

Hvis passenger kan være NULL, er det vigtigt at tilføje NULLS LAST :

Fra flere passagernavne med samme maksimale længde i samme gruppe får du et vilkårligt valg - medmindre du tilføjer flere udtryk til ORDER BY som tiebreaker. Detaljeret forklaring i svaret, der er linket ovenfor.

Ydeevne?

Typisk er en enkelt scanning overlegen, især med sekventielle scanninger.

Ovenstående forespørgsel bruger to scanninger (måske indeks-/indeksscanninger). Men den anden scanning er forholdsvis billig, medmindre bordet er for stort til at passe i cachen (for det meste). Lukas foreslog en alternativ forespørgsel med kun en enkelt SELECT tilføjer:

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

Ideen er smart, men sidste gang jeg testede , array_agg med ORDER BY ikke præsterede så godt. (Overhead for pr-gruppe ORDER BY er betydelig, og array-håndtering er også dyr.)

Den samme tilgang kan være billigere med en tilpasset aggregeret funktion first() som instrueret i Postgres Wiki her . Eller endnu hurtigere med en version skrevet i C, tilgængelig på PGXN . Eliminerer de ekstra omkostninger til array-håndtering, men vi har stadig brug for pr-gruppe ORDER BY . Kan være hurtigere kun for få grupper. Du vil derefter tilføje:

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

Gordon og Lukas nævne også vinduesfunktionen first_value() . Vinduesfunktioner anvendes efter aggregerede funktioner. For at bruge det i den samme SELECT , skal vi samle passenger på en eller anden måde først - catch 22. Gordon løser dette med en underforespørgsel - en anden kandidat til god præstation med standard Postgres.

first() gør det samme uden underforespørgsel og burde være enklere og en smule hurtigere. Men det vil stadig ikke være hurtigere end en separat DISTINCT ON for de fleste tilfælde med få rækker pr. gruppe. For mange rækker pr. gruppe er en rekursiv CTE-teknik typisk hurtigere. Der er endnu hurtigere teknikker, hvis du har en separat tabel, der indeholder alle relevante, unikke orig værdier. Detaljer:

Den bedste løsning afhænger af forskellige faktorer. Beviset for buddingen er i spisningen. For at optimere ydeevnen skal du teste med din opsætning. Ovenstående forespørgsel burde være blandt de hurtigste.



  1. SQL Server 2005 ROW_NUMBER() uden ORDER BY

  2. SQL-ydeevne søger efter lange strenge

  3. Sådan beregnes forskellen mellem to datoer i T-SQL

  4. Live opdatering MySQL Data