PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Използвайте нещо като TOP с GROUP BY

Можете удобно да извлечете пътника с най-дългото име за група с 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 стойности. Подробности:

Най-доброто решение зависи от различни фактори. Доказателството за пудинга е в яденето. За да оптимизирате производителността, трябва да тествате с вашата настройка. Горната заявка трябва да е сред най-бързите.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Защо защитата на ниво ред не е активирана за изгледи на Postgres?

  2. DatabaseError:текущата транзакция е прекратена, командите се игнорират до края на блока на транзакцията?

  3. Как да стартирате PostgreSQL Server на Mac OS X чрез Homebrew

  4. postgresql уникално ограничение за всяко цяло число от две колони (или от масив)

  5. Typecast низ към цяло число