DISTINCT — это команда «не возвращай повторяющиеся строки». Самый простой способ получить уникальные значения колонки или комбинации колонок: список стран, в которых живут пользователи, список тегов на блоге, набор уникальных дат заказов.
Без DISTINCT запрос вернёт всё подряд — и если у тебя 1000 заказов из 50 стран, то 1000 строк со страной. С DISTINCT — 50 строк, по одной на страну.
Зачем нужен DISTINCT
Самый частый сценарий: «дай мне все уникальные значения этой колонки», без счёта, без агрегатов, просто список. DISTINCT отвечает на это в одну строку:
- Список регионов, откуда заходят пользователи (для UI-фильтра).
- Список тегов в блоге (для облака тегов).
- Уникальные даты, по которым были продажи.
Базовый синтаксис
SELECT DISTINCT country FROM users;
Postgres соберёт все значения country, отсеет дубликаты, вернёт по одной строке на уникальное значение.
Пример с таблицей
Таблица users:
| id |
name |
country |
| 1 |
Аня |
RU |
| 2 |
Боб |
US |
| 3 |
Вера |
RU |
| 4 |
Гриша |
RU |
| 5 |
Дима |
US |
| 6 |
Лена |
BY |
Без DISTINCT:
SELECT country FROM users;
| country |
| RU |
| US |
| RU |
| RU |
| US |
| BY |
С DISTINCT:
SELECT DISTINCT country FROM users;
Шесть строк превратились в три — по одной на уникальную страну.
DISTINCT по нескольким колонкам
SELECT DISTINCT country, tier FROM users;
Уникальной считается пара (country, tier). Если есть (RU, free) и ещё одна (RU, free) — отсеется одна. Но (RU, free) и (RU, gold) — это разные пары, обе останутся.
Результат на нашей таблице (если у Ани, Веры, Гриши tier разные):
| country |
tier |
| RU |
free |
| RU |
gold |
| US |
free |
| BY |
gold |
DISTINCT vs GROUP BY
Часто их путают. Оба «схлопывают» дубликаты:
SELECT DISTINCT country FROM users — даёт уникальные страны.
SELECT country FROM users GROUP BY country — то же самое.
Если не нужен агрегат, по производительности и читаемости они эквивалентны. Большинство планировщиков (включая Postgres) выполняют их одинаково.
Когда нужен агрегат — только GROUP BY:
SELECT country, COUNT(*) FROM users GROUP BY country;
Правило: если просто уникальные — DISTINCT короче и яснее. Если уникальные + счётчик — GROUP BY.
DISTINCT с NULL
Все NULL'ы в DISTINCT считаются одним и тем же значением. Если у тебя 5 строк с email IS NULL, в DISTINCT email будет одна строка NULL:
SELECT DISTINCT email FROM users;
Если хочется «без NULL вообще» — WHERE email IS NOT NULL.
DISTINCT ON — Postgres-фишка
PostgreSQL поддерживает DISTINCT ON (column) — «по одной строке на каждое уникальное значение column». Это уже не «убери дубликаты», а «оставь первую из каждой группы».
SELECT DISTINCT ON (customer_id) *
FROM orders
ORDER BY customer_id, created_at DESC;
Логика:
- Postgres сортирует строки по
customer_id, created_at DESC.
- Для каждого уникального
customer_id берёт первую в этой сортировке (т.е. с самой свежей датой).
- Возвращает только эти строки — по одной на клиента.
Это типичный паттерн «топ-1 в каждой группе». В стандартном SQL аналогичное делается через ROW_NUMBER + подзапрос — длиннее.
DISTINCT ON есть только в PostgreSQL. Не работает в MySQL, SQLite, MSSQL.
DISTINCT внутри агрегата
В агрегатных функциях можно использовать DISTINCT:
SELECT COUNT(DISTINCT customer_id) FROM orders;
Это считает уникальные значения customer_id, а не все строки таблицы. Самый частый use case в аналитике.
Частые ошибки новичков
1. DISTINCT действует на ВСЕ колонки в SELECT. Думаешь «уберу дубликаты только по country», пишешь SELECT DISTINCT country, name FROM users — но (RU, Аня) и (RU, Вера) это разные пары, дубликаты не схлопнутся. Если хочешь уникальное по одной колонке — пиши SELECT DISTINCT country без name.
2. DISTINCT не сортирует. SELECT DISTINCT country FROM users вернёт страны в произвольном порядке. Хочешь алфавитно — ORDER BY country явно.
3. Производительность на больших таблицах. DISTINCT требует от БД отсортировать (или построить хеш) по всему набору. На таблице 100M строк может занять долго. Часто помогает индекс на колонке.
4. COUNT(DISTINCT *) не работает. Можно COUNT(*) или COUNT(DISTINCT column) — но не COUNT(DISTINCT *). Для подсчёта уникальных строк целиком — оборачивай в подзапрос:
SELECT COUNT(*) FROM (SELECT DISTINCT * FROM users) t;
5. NULL = NULL в DISTINCT. В большинстве сравнений SQL NULL = NULL даёт NULL (= не равно). Но в DISTINCT (а также в GROUP BY) NULL'ы группируются вместе. Это исключение, и о нём часто забывают.
6. DISTINCT ON без правильного ORDER BY. Для DISTINCT ON (customer_id) сортировка должна начинаться с customer_id. ORDER BY created_at DESC без customer_id даст ошибку. Правильно: ORDER BY customer_id, created_at DESC.
Мини-резюме
DISTINCT убирает дубликаты — возвращает уникальные строки.
- По умолчанию учитывает все колонки в SELECT. Хочешь по одной — клади только её.
- NULL'ы в
DISTINCT = одно значение. Если хочешь без NULL — WHERE column IS NOT NULL.
DISTINCT без агрегатов ≈ GROUP BY без агрегатов. Используй DISTINCT для краткости, GROUP BY — когда нужен COUNT/SUM/AVG.
DISTINCT ON (col) — Postgres-фишка для «топ-1 в каждой группе». В сочетании с правильным ORDER BY.
DISTINCT— это команда «не возвращай повторяющиеся строки». Самый простой способ получить уникальные значения колонки или комбинации колонок: список стран, в которых живут пользователи, список тегов на блоге, набор уникальных дат заказов.Без
DISTINCTзапрос вернёт всё подряд — и если у тебя 1000 заказов из 50 стран, то 1000 строк со страной. СDISTINCT— 50 строк, по одной на страну.Зачем нужен DISTINCT
Самый частый сценарий: «дай мне все уникальные значения этой колонки», без счёта, без агрегатов, просто список.
DISTINCTотвечает на это в одну строку:Базовый синтаксис
SELECT DISTINCT country FROM users;Postgres соберёт все значения
country, отсеет дубликаты, вернёт по одной строке на уникальное значение.Пример с таблицей
Таблица
users:Без
DISTINCT:SELECT country FROM users;С
DISTINCT:SELECT DISTINCT country FROM users;Шесть строк превратились в три — по одной на уникальную страну.
DISTINCT по нескольким колонкам
SELECT DISTINCT country, tier FROM users;Уникальной считается пара
(country, tier). Если есть(RU, free)и ещё одна(RU, free)— отсеется одна. Но(RU, free)и(RU, gold)— это разные пары, обе останутся.Результат на нашей таблице (если у Ани, Веры, Гриши tier разные):
DISTINCT vs GROUP BY
Часто их путают. Оба «схлопывают» дубликаты:
SELECT DISTINCT country FROM users— даёт уникальные страны.SELECT country FROM users GROUP BY country— то же самое.Если не нужен агрегат, по производительности и читаемости они эквивалентны. Большинство планировщиков (включая Postgres) выполняют их одинаково.
Когда нужен агрегат — только
GROUP BY:-- DISTINCT не подходит, нужен COUNT SELECT country, COUNT(*) FROM users GROUP BY country;Правило: если просто уникальные —
DISTINCTкороче и яснее. Если уникальные + счётчик —GROUP BY.DISTINCT с NULL
Все NULL'ы в
DISTINCTсчитаются одним и тем же значением. Если у тебя 5 строк сemail IS NULL, вDISTINCT emailбудет одна строкаNULL:-- В таблице 100 строк, у 30 email = NULL, у 70 — заполнен (50 уникальных) SELECT DISTINCT email FROM users; -- → 51 строка: 50 уникальных email + одна NULLЕсли хочется «без NULL вообще» —
WHERE email IS NOT NULL.DISTINCT ON — Postgres-фишка
PostgreSQL поддерживает
DISTINCT ON (column)— «по одной строке на каждое уникальное значениеcolumn». Это уже не «убери дубликаты», а «оставь первую из каждой группы».-- Самый свежий заказ каждого клиента SELECT DISTINCT ON (customer_id) * FROM orders ORDER BY customer_id, created_at DESC;Логика:
customer_id, created_at DESC.customer_idберёт первую в этой сортировке (т.е. с самой свежей датой).Это типичный паттерн «топ-1 в каждой группе». В стандартном SQL аналогичное делается через
ROW_NUMBER+ подзапрос — длиннее.DISTINCT ONесть только в PostgreSQL. Не работает в MySQL, SQLite, MSSQL.DISTINCT внутри агрегата
В агрегатных функциях можно использовать
DISTINCT:-- Сколько уникальных юзеров что-то заказывали SELECT COUNT(DISTINCT customer_id) FROM orders;Это считает уникальные значения
customer_id, а не все строки таблицы. Самый частый use case в аналитике.Частые ошибки новичков
1.
DISTINCTдействует на ВСЕ колонки в SELECT. Думаешь «уберу дубликаты только поcountry», пишешьSELECT DISTINCT country, name FROM users— но(RU, Аня)и(RU, Вера)это разные пары, дубликаты не схлопнутся. Если хочешь уникальное по одной колонке — пишиSELECT DISTINCT countryбезname.2.
DISTINCTне сортирует.SELECT DISTINCT country FROM usersвернёт страны в произвольном порядке. Хочешь алфавитно —ORDER BY countryявно.3. Производительность на больших таблицах.
DISTINCTтребует от БД отсортировать (или построить хеш) по всему набору. На таблице 100M строк может занять долго. Часто помогает индекс на колонке.4.
COUNT(DISTINCT *)не работает. МожноCOUNT(*)илиCOUNT(DISTINCT column)— но неCOUNT(DISTINCT *). Для подсчёта уникальных строк целиком — оборачивай в подзапрос:SELECT COUNT(*) FROM (SELECT DISTINCT * FROM users) t;5. NULL = NULL в DISTINCT. В большинстве сравнений SQL
NULL = NULLдаётNULL(= не равно). Но вDISTINCT(а также вGROUP BY) NULL'ы группируются вместе. Это исключение, и о нём часто забывают.6.
DISTINCT ONбез правильногоORDER BY. ДляDISTINCT ON (customer_id)сортировка должна начинаться сcustomer_id.ORDER BY created_at DESCбезcustomer_idдаст ошибку. Правильно:ORDER BY customer_id, created_at DESC.Мини-резюме
DISTINCTубирает дубликаты — возвращает уникальные строки.DISTINCT= одно значение. Если хочешь без NULL —WHERE column IS NOT NULL.DISTINCTбез агрегатов ≈GROUP BYбез агрегатов. ИспользуйDISTINCTдля краткости,GROUP BY— когда нуженCOUNT/SUM/AVG.DISTINCT ON (col)— Postgres-фишка для «топ-1 в каждой группе». В сочетании с правильнымORDER BY.