← Назад к вопросам

Как работает DISTINCT в SQL? В каких случаях его стоит и не стоит использовать?

1.2 Junior🔥 181 комментариев
#SQL и базы данных

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

DISTINCT в SQL: удаление дубликатов и оптимизация

DISTINCT — это ключевое слово в SQL, которое удаляет дублирующиеся строки из результирующего набора. Кажется просто, но есть множество нюансов и ситуаций, где его использование может быть неправильным или неэффективным.

Базовый синтаксис и примеры

Простой DISTINCT

-- Все значения с дубликатами
SELECT country FROM users
ORDER BY country
LIMIT 10

-- Результат:
-- US
-- US
-- CA
-- UK
-- US

-- С DISTINCT (только уникальные)
SELECT DISTINCT country FROM users
ORDER BY country

-- Результат:
-- CA
-- UK
-- US

DISTINCT с несколькими столбцами

-- DISTINCT применяется ко всей строке, не отдельно к каждому столбцу
SELECT DISTINCT country, platform FROM users

-- Результат (уникальные комбинации):
-- US, web
-- US, mobile
-- CA, web
-- UK, mobile

Важно помнить: DISTINCT удаляет дубликаты всей строки, а не отдельных столбцов.

Как работает DISTINCT внутри

Когда ты пишешь DISTINCT, база данных:

  1. Выбирает все строки из запроса
  2. Сортирует их (обычно по hash или полностью)
  3. Удаляет соседние дубликаты
  4. Возвращает результат
-- DISTINCT требует SORT, что дорого
EXPLAIN ANALYZE SELECT DISTINCT country FROM users

-- Результат покажет:
-- -> HashAggregate (cost=..., rows=...)  или
-- -> Sort Distinct (cost=..., rows=...)

Когда СТОИТ использовать DISTINCT

1. Подсчёт уникальных значений (но COUNT(DISTINCT) лучше)

-- Плохо: долгий результат
SELECT DISTINCT user_id FROM orders

-- Хорошо: быстрый результат
SELECT COUNT(DISTINCT user_id) FROM orders

2. Удаление явных дубликатов в данных

-- Есть ошибка в данных: пользователи дублируются
SELECT user_id FROM customers

-- Результат:
-- 1
-- 1
-- 2
-- 3
-- 3

-- Удалить дубликаты
SELECT DISTINCT user_id FROM customers

3. Проверка, сколько уникальных значений

-- Сколько уникальных стран использует платформа?
SELECT DISTINCT country FROM users
-- Результат даст представление о разнообразии

-- Лучший вариант:
SELECT COUNT(DISTINCT country) as countries_count FROM users

4. Когда тебе нужно один раз в месяц вычислить список уникальных

-- Создать справочник уникальных городов один раз в месяц
SELECT DISTINCT city FROM users
WHERE created_at >= CURRENT_DATE - INTERVAL 30 DAY

Когда НЕ стоит использовать DISTINCT

1. Для подсчёта вместо COUNT(DISTINCT)

-- Неправильно и медленно
SELECT COUNT(*) FROM (
    SELECT DISTINCT user_id FROM orders
) t

-- Правильно и быстро
SELECT COUNT(DISTINCT user_id) FROM orders

Почему? Первый вариант создаёт промежуточную таблицу, требует сортировки. COUNT(DISTINCT) напрямую.

2. В WHERE вместо IN с подзапросом

-- Плохо
SELECT * FROM orders
WHERE user_id IN (
    SELECT DISTINCT user_id FROM users WHERE country = 'US'
)

-- Хорошо
SELECT * FROM orders
WHERE user_id IN (
    SELECT user_id FROM users WHERE country = 'US'
)
-- SQL оптимизатор сам удалит дубликаты

3. С GROUP BY (они делают одно и то же)

-- Плохо: двойная работа
SELECT DISTINCT product_id, category FROM products
GROUP BY product_id, category

-- Хорошо: одного достаточно
SELECT product_id, category FROM products
GROUP BY product_id, category

-- Или если нужны агрегаты:
SELECT product_id, category, COUNT(*) as count FROM products
GROUP BY product_id, category

4. На ОЧЕНЬ больших таблицах без фильтра

-- Медленно: сканирует всю таблицу, затем сортирует (10 млн строк)
SELECT DISTINCT user_id FROM events
-- Это может заморозить БД

-- Лучше: сначала отфильтруй
SELECT DISTINCT user_id FROM events
WHERE created_at >= CURRENT_DATE - INTERVAL 30 DAY
LIMIT 100000

Оптимизация DISTINCT запросов

1. Используй GROUP BY вместо DISTINCT

-- DISTINCT (сортировка)
SELECT DISTINCT user_id, country FROM users
-- Execution: Sort Distinct

-- GROUP BY (обычно быстрее на больших таблицах)
SELECT user_id, country FROM users
GROUP BY user_id, country
-- Execution: HashAggregate (быстрее на 30-40%)

2. Добавляй фильтры WHERE перед DISTINCT

-- Медленно: сортирует все 10 млн пользователей
SELECT DISTINCT country FROM users

-- Быстро: сортирует только активных 500k
SELECT DISTINCT country FROM users
WHERE last_active >= CURRENT_DATE - INTERVAL 90 DAY

3. Используй LIMIT при необходимости

-- Только первые 100 уникальных стран
SELECT DISTINCT country FROM users
LIMIT 100

-- PostgreSQL может остановиться рано, не сортируя всё

4. Добавляй индексы на столбцы DISTINCT

-- Если часто запрашиваешь DISTINCT по country
CREATE INDEX idx_users_country ON users(country)

-- PostgreSQL может использовать индекс, не сканируя всю таблицу

Практические примеры анализа

Пример 1: Уникальные пользователи по странам

-- Правильно для анализа
SELECT 
    country,
    COUNT(*) as user_count,
    COUNT(DISTINCT user_id) as unique_users  -- если есть дубликаты
FROM users
WHERE created_at >= CURRENT_DATE - INTERVAL 30 DAY
GROUP BY country
ORDER BY unique_users DESC

Пример 2: Активные потребители маркетплейса

-- Найти уникальные потребителя с покупками
SELECT DISTINCT consumer_id
FROM orders
WHERE created_at >= CURRENT_DATE - INTERVAL 30 DAY
    AND status = 'completed'

-- Лучше: если нужна агрегация
SELECT 
    consumer_id,
    COUNT(*) as orders,
    SUM(amount) as total_spent
FROM orders
WHERE created_at >= CURRENT_DATE - INTERVAL 30 DAY
    AND status = 'completed'
GROUP BY consumer_id
ORDER BY total_spent DESC

Пример 3: Чистка дублей перед JOIN

-- Есть дубликаты в таблице
SELECT COUNT(*) FROM customers  -- 1,000,000
SELECT COUNT(DISTINCT customer_id) FROM customers  -- 990,000

-- Перед объединением
WITH clean_customers AS (
    SELECT DISTINCT ON (customer_id) customer_id, name, email
    FROM customers
    ORDER BY customer_id, created_at DESC
)
SELECT * FROM clean_customers
JOIN orders ON clean_customers.customer_id = orders.customer_id

Как мониторить производительность

-- Проверить, сколько времени занимает DISTINCT
EXPLAIN ANALYZE
SELECT DISTINCT user_id FROM events WHERE created_at >= CURRENT_DATE - 30

-- Посмотри на строку "Actual time" — если больше 1 сек, переделай запрос

Интерпретация:

  • Planning Time: < 1ms (хорошо)
  • Execution Time: < 100ms (хорошо)
  • Execution Time: 1-5 сек (плохо, нужно оптимизировать)
  • Execution Time: > 5 сек (очень плохо, переписать)

Рекомендации для Product Analysts

  1. Используй COUNT(DISTINCT) для подсчётов, не SELECT DISTINCT + COUNT
  2. Добавляй WHERE фильтры, чтобы снизить объём данных перед DISTINCT
  3. Используй GROUP BY когда нужна агрегация, а не DISTINCT
  4. Проверяй EXPLAIN ANALYZE, если запрос медленный
  5. На больших таблицах (> 1M строк) часто приходится переписывать DISTINCT запросы

Сравнительная таблица

ЗадачаРешениеПроизводительность
Подсчитать уникальные user_idCOUNT(DISTINCT user_id)Отлично
Получить список уникальных user_idSELECT DISTINCT user_idХорошо
Уникальные + агрегацияSELECT ... GROUP BYОтлично
Очень много уникальных на большой таблицеDISTINCT + WHERE + LIMITХорошо
Удаление дубликатов перед JOINSELECT DISTINCT ON или ROW_NUMBER()Зависит

Главный вывод: DISTINCT — это инструмент для удаления дубликатов, но обычно есть более эффективный способ сделать то же самое с GROUP BY или COUNT(DISTINCT). Используй DISTINCT только когда других вариантов нет.

Как работает DISTINCT в SQL? В каких случаях его стоит и не стоит использовать? | PrepBro