sql >> Database >  >> RDS >> PostgreSQL

Gebruik zoiets als TOP met GROUP BY

U kunt per groep gemakkelijk de passagier met de langste naam terugvinden met DISTINCT ON .

Maar ik zie geen manier om dat (of een andere eenvoudige manier) te combineren met uw oorspronkelijke zoekopdracht in een enkele SELECT . Ik stel voor om twee afzonderlijke subquery's samen te voegen:

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 in de join-clausule wordt handig slechts één instantie van orig . uitgevoerd , dus je kunt gewoon SELECT * . gebruiken in de buitenste SELECT .

Als passenger kan NULL zijn, het is belangrijk om NULLS LAST toe te voegen :

Van meerdere passagiersnamen met dezelfde maximale lengte in dezelfde groep, krijg je een willekeurige keuze - tenzij u meer uitdrukkingen toevoegt aan ORDER BY als tiebreak. Gedetailleerde uitleg in het antwoord dat hierboven is gelinkt.

Prestaties?

Gewoonlijk is een enkele scan superieur, vooral bij opeenvolgende scans.

De bovenstaande zoekopdracht gebruikt twee scans (misschien index / alleen-index scans). Maar de tweede scan is relatief goedkoop, tenzij de tafel te groot is om in de cache te passen (meestal). Lukas stelde een alternatieve zoekopdracht voor met slechts een enkele SELECT toevoegen:

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

Het idee is slim, maar laatste keer dat ik heb getest , array_agg met ORDER BY presteerde niet zo goed. (De overhead van ORDER BY per groep is aanzienlijk, en het hanteren van arrays is ook duur.)

Dezelfde aanpak kan goedkoper zijn met een aangepaste aggregatiefunctie first() zoals aangegeven in de Postgres Wiki hier . Of, nog sneller, met een versie geschreven in C, beschikbaar op PGXN . Elimineert de extra kosten voor het afhandelen van arrays, maar we hebben nog steeds ORDER BY per groep nodig . Mogelijk sneller voor slechts enkele groepen. Je zou dan toevoegen:

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

Gordon en Lukas vermeld ook de vensterfunctie first_value() . Vensterfuncties worden na . toegepast geaggregeerde functies. Om het te gebruiken in dezelfde SELECT , moeten we passenger bij elkaar optellen op de een of andere manier eerst - catch 22. Gordon lost dit op met een subquery - een andere kandidaat voor goede prestaties met standaard Postgres.

first() doet hetzelfde zonder subquery en zou eenvoudiger en een beetje sneller moeten zijn. Maar het zal nog steeds niet sneller zijn dan een aparte DISTINCT ON voor de meeste gevallen met weinig rijen per groep. Voor veel rijen per groep is een recursieve CTE-techniek doorgaans sneller. Er zijn nog snellere technieken als je een aparte tabel hebt met alle relevante, unieke orig waarden. Details:

De beste oplossing hangt af van verschillende factoren. Het bewijs van de pudding zit in het eten. Om de prestaties te optimaliseren, moet je testen met je setup. De bovenstaande zoekopdracht zou een van de snelste moeten zijn.



  1. Datumtijd in PHP-script

  2. Erlang en PostgreSQL

  3. Fout:kon de hoofdinformatiestructuur niet initialiseren tijdens het uitvoeren van Master Slave-replicatie in MySQL

  4. Zet een database neer of maak deze aan vanuit de opgeslagen procedure in PostgreSQL