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.