Можете удобно да извлечете пътника с най-дългото име за група с DISTINCT ON
.
Но не виждам начин да комбинирам това (или друг прост начин) с оригиналната ви заявка в един SELECT
. Предлагам да се съединят две отделни подзаявки:
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
в клаузата за присъединяване удобно извежда само един екземпляр на orig
, така че можете просто да използвате SELECT *
във външния SELECT
.
Ако passenger
може да бъде NULL, важно е да добавите NULLS LAST
:
От множество имена на пътници с еднаква максимална дължина в една и съща група получавате произволен избор - освен ако не добавите още изрази към ORDER BY
като тайбрек. Подробно обяснение в отговора, свързан по-горе.
Ефективност?
Обикновено едно сканиране е по-добро, особено при последователни сканирания.
Горната заявка използва две сканирания (може би индексни/само индексни сканирания). Но второто сканиране е сравнително евтино, освен ако таблицата не е твърде голяма, за да се побере в кеша (най-вече). Лукас предложи алтернативна заявка само с единствена SELECT
добавяне:
, (ARRAY_AGG (passenger ORDER BY LENGTH (passenger) DESC))[1] -- I'd add NULLS LAST
Идеята е умна, но последния път, когато тествах
, array_agg
с ORDER BY
не се представи толкова добре. (Разходите за всяка група ORDER BY
е значителна, а обработката на масиви също е скъпа.)
Същият подход може да бъде по-евтин с персонализирана агрегатна функция first()
както е указано в Postgres Wiki тук
. Или още по-бързо с версия, написана на C, достъпна на PGXN
. Елиминира допълнителните разходи за обработка на масиви, но все още се нуждаем от група ORDER BY
. Може да е по-бързо само за няколко групи. След това бихте добавили:
, first(passenger ORDER BY length(passenger) DESC NULLS LAST)
Гордън
и Лукас
споменете също функцията за прозорец first_value()
. Функциите на прозореца се прилагат след агрегатни функции. За да го използвате в същия SELECT
, ще трябва да обобщим passenger
по някакъв начин първо - улов 22. Гордън решава това с подзаявка - друг кандидат за добра производителност със стандартния Postgres.
first()
прави същото без подзапитване и трябва да е по-просто и малко по-бързо. Но пак няма да е по-бързо от отделно DISTINCT ON
за повечето случаи с няколко реда на група. За много редове на група рекурсивната CTE техника обикновено е по-бърза. Има още по-бързи техники, ако имате отделна таблица, съдържаща всички подходящи, уникални orig
стойности. Подробности:
Най-доброто решение зависи от различни фактори. Доказателството за пудинга е в яденето. За да оптимизирате производителността, трябва да тествате с вашата настройка. Горната заявка трябва да е сред най-бързите.