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 свързани реда на ред
Референтни показатели
Вижте отделен отговор.