DISTINCT и GROUP BY в SQL: что выбрать для уникальных значений
Разбираем разницу DISTINCT и GROUP BY в SQL: список уникальных пользователей, дедупликация событий, агрегаты и риск скрыть проблему в данных.
Содержание статьи
SELECT DISTINCT часто становится быстрым лекарством от повторяющихся строк. Но повтор может быть легитимным событием, дублем из-за JOIN или результатом неверного зерна. Если убрать его без диагностики, отчёт станет красивее, а источник ошибки останется. Разберём вопрос на одном запросе: Когда DISTINCT действительно убирает дубли, а когда прячет ошибку в событийных данных?
Когда нужен этот приём
Когда DISTINCT действительно убирает дубли, а когда прячет ошибку в событийных данных?
Для списка пользователей, открывших экран, DISTINCT user_id уместен, если событие может повторяться. Для проверки дублей сначала посчитай COUNT(*) и COUNT(DISTINCT event_id) по ключу. Для выручки нельзя использовать DISTINCT amount: два одинаковых заказа — не дубликаты только потому, что суммы совпали.
Выбирай DISTINCT, когда правило уникальности понятно. Выбирай GROUP BY, когда результатом являются группы и агрегаты. Для качества данных сначала показывай дубли отдельным запросом, а не сразу удаляй их в финальном отчёте.
Логика оператора
DISTINCT оставляет уникальные комбинации выбранных колонок. GROUP BY формирует группы и нужен, когда нужно посчитать, суммировать или вычислить другие агрегаты. Запросы иногда дают одинаковый результат, но различаются намерением и возможностью расширения.
Перед DISTINCT назови ключ уникальности. Для событий это может быть event_id, user_id + event_name + occurred_at или отдельный idempotency key. Сравни количество строк до и после удаления, найди примеры повторов и реши, что делать с ними: оставить, дедуплицировать или исправить загрузку.
- Зафиксируй одну строку результата.
- Отдели условия по исходным строкам от условий по рассчитанным значениям.
- Назови период, ключи связи и правило для повторов.
Рабочая версия
Названия таблиц здесь условные. При переносе сохрани зерно и бизнес-условия, а не буквальный синтаксис примера.
SELECT event_name, COUNT(*) AS rows_count,
COUNT(DISTINCT event_id) AS unique_events,
COUNT(DISTINCT user_id) AS users
FROM events
WHERE occurred_at >= TIMESTAMP '2026-07-01 00:00:00'
GROUP BY event_name
ORDER BY rows_count DESC;Как сверить ответ
Собери пять-десять строк, где ответ можно получить вручную. Добавь NULL, дубль, объект без связанной записи и значение на границе периода. Затем сравни число строк и уникальных ключей до и после спорного шага.
Выполненный без ошибки запрос ещё не является правильным. Другой аналитик должен суметь восстановить из него источник, фильтр, grain и ожидаемое поведение на крайних случаях.
| Выражение | Считает | Когда нужно |
|---|---|---|
| COUNT(*) | строки | объём событий или заказов |
| COUNT(DISTINCT event_id) | уникальные события | аудит загрузки |
| COUNT(DISTINCT user_id) | людей | активная аудитория |
| GROUP BY user_id | группы пользователей | метрики по пользователю |
Когда выбрать другой способ
DISTINCT по всем колонкам не удалит строки, где отличается техническое поле. DISTINCT в агрегате меняет смысл: COUNT(DISTINCT user_id) считает людей, COUNT(*) — события. Не используй DISTINCT после подозрительного JOIN, пока не проверил кардинальность связи.
Выбирай DISTINCT, когда правило уникальности понятно. Выбирай GROUP BY, когда результатом являются группы и агрегаты. Для качества данных сначала показывай дубли отдельным запросом, а не сразу удаляй их в финальном отчёте.
Разница между строками, событиями и пользователями показывает, что именно измеряет метрика.
Небольшой эксперимент
Возьми таблицу событий и сделай аудит: число строк, уникальные event_id, пользователи, события по минутам. Найди случай, где один пользователь совершил одно действие несколько раз законно. Затем добавь дублирующую строку и проверь, как меняется метрика.
- Сначала запиши ожидаемый результат словами.
- Проверь запрос на маленькой контрольной выборке.
- Объясни, что запрос считает и чего не доказывает.
Следующий шаг
Выбирай DISTINCT, когда правило уникальности понятно. Выбирай GROUP BY, когда результатом являются группы и агрегаты. Для качества данных сначала показывай дубли отдельным запросом, а не сразу удаляй их в финальном отчёте.
Материалы по теме
WHERE и HAVING в SQL: разница на примерах аналитики
Понятное сравнение WHERE и HAVING в SQL: фильтрация строк до GROUP BY, фильтрация групп после агрегации и типичные ошибки аналитика.
Кардинальность JOIN в SQL: как не умножить строки и выручку
Как проверить связь один-к-одному, один-ко-многим и многие-ко-многим в SQL и избежать завышенных метрик после JOIN.

SQL GROUP BY и COUNT: как считать пользователей по каналам и дням
Разбираем GROUP BY, COUNT и COUNT DISTINCT на задачах аналитика: пользователи по каналам, DAU по дням и фильтрация агрегатов через HAVING.