DISTINCT ON обикновено е най-простият и бърз за това в PostgreSQL .
(За оптимизиране на производителността за определени натоварвания вижте по-долу.)
SELECT DISTINCT ON (customer)
id, customer, total
FROM purchases
ORDER BY customer, total DESC, id; Или по-кратко (ако не е толкова ясно) с поредни номера на изходните колони:
SELECT DISTINCT ON (2)
id, customer, total
FROM purchases
ORDER BY 2, 3 DESC, 1;
Ако total може да бъде NULL (в никакъв случай няма да навреди, но ще искате да съпоставите съществуващите индекси):
...
ORDER BY customer, total DESC NULLS LAST, id; Основни точки
DISTINCT ON е PostgreSQL разширение на стандарта (където само DISTINCT като цяло SELECT списъкът е дефиниран).
Избройте произволен брой изрази в DISTINCT ON клауза, комбинираната стойност на ред дефинира дубликати. Ръководството:
Очевидно два реда се считат за различни, ако се различават в поне една стойност на колоната. Нулевите стойности се считат за равни в това сравнение.
Удебелен акцент мой.
DISTINCT ON може да се комбинира с ORDER BY . Водещи изрази в ORDER BY трябва да е в набора от изрази в DISTINCT ON , но можете свободно да пренареждате реда между тях. Пример.
Можете да добавите допълнителни изрази към ORDER BY за да изберете конкретен ред от всяка група връстници. Или, както се казва в ръководството:
DISTINCT ONизраз(ите) трябва да съвпадат с най-лявотоORDER BYизраз(и).ORDER BYклаузата обикновено съдържа допълнителен(и) израз(и), които определят желания приоритет на редовете във всекиDISTINCT ONгрупа.
Добавих id като последен елемент за прекъсване на връзките:
"Изберете реда с най-малкия id от всяка група, споделяща най-висок total ."
За да подредите резултатите по начин, който не е съгласен с реда на сортиране, определящ първия за група, можете да вмъкнете над заявката във външна заявка с друга ORDER BY . Пример.
Ако total може да бъде NULL, вие най-вероятно искате реда с най-голямата стойност, различна от нула. Добавете NULLS LAST като демонстрирано. Вижте:
- Сортиране по колона ASC, но първо NULL стойности?
SELECT списъка не е ограничен от изрази в DISTINCT ON или ORDER BY по всякакъв начин. (Не е необходимо в простия случай по-горе):
-
Не е нужно включва някой от изразите в
DISTINCT ONилиORDER BY. -
Вие можете включете всеки друг израз в
SELECTсписък. Това е важно за замяната на много по-сложни заявки с подзаявки и агрегатни/прозоречни функции.
Тествах с Postgres версии 8.3 – 13. Но функцията е налице поне от версия 7.1, така че по принцип винаги.
Индекс
Перфектният индексът за горната заявка ще бъде индекс с няколко колони, обхващащ всичките три колони в съвпадаща последователност и със съответстващ ред на сортиране:
CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);
Може да е твърде специализиран. Но използвайте го, ако производителността на четене за конкретната заявка е от решаващо значение. Ако имате DESC NULLS LAST в заявката, използвайте същото в индекса, така че редът на сортиране да съвпада и индексът да е приложим.
Ефективност/Оптимизация на производителността
Претеглете разходите и ползите, преди да създадете персонализирани индекси за всяка заявка. Потенциалът на горния индекс до голяма степен зависи от разпределението на данните .
Индексът се използва, защото предоставя предварително сортирани данни. В Postgres 9.2 или по-нова версия, заявката може да се възползва и от сканиране само на индекс ако индексът е по-малък от основната таблица. Индексът обаче трябва да бъде сканиран изцяло.
Заняколко редове на клиента (висока мощност в колона customer ), това е много ефективно. Още повече, ако все пак имате нужда от сортиран изход. Ползата се свива с нарастващ брой редове на клиент.
В идеалния случай имате достатъчно work_mem да обработи включената стъпка на сортиране в RAM и да не се разлее на диск. Но обикновено се задава work_mem също високо може да има неблагоприятни последици. Помислете за SET LOCAL за изключително големи запитвания. Намерете колко ви трябва с EXPLAIN ANALYZE . Споменаване на „Диск: " в стъпката за сортиране показва необходимостта от повече:
- Конфигурационен параметър work_mem в PostgreSQL на Linux
- Оптимизирайте простата заявка, като използвате ПОРЪЧАЙТЕ ПО дата и текст
Замного редове на клиента (ниска мощност в колона customer ), разхлабено сканиране на индекс (известен още като „пропускане на сканиране“) би било (много) по-ефективно, но това не се прилага до Postgres 14. (Разработва се реализация за сканиране само с индекс за Postgres 15. Вижте тук и тук.)
За Postgres 15. сега има по-бързи техники за заявка да замести това. По-специално, ако имате отделна маса с уникални клиенти, което е типичният случай на употреба. Но също и ако не го направите:
- SELECT DISTINCT е по-бавен от очакваното в моята таблица в PostgreSQL
- Оптимизиране на заявката GROUP BY за извличане на последния ред на потребител
- Оптимизиране на груповата максимална заявка
- Запитване за последните N свързани реда на ред
Референтни показатели
Вижте отделен отговор.