При устранении дублей в отчёте что принципиально различает DISTINCT и GROUP BY без агрегатов?

При устранении дублей в отчёте что принципиально различает DISTINCT и GROUP BY без агрегатов?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

DISTINCT удаляет повторяющиеся строки из уже сформированного набора выбранных выражений. GROUP BY разбивает строки на группы по указанным ключам; даже без агрегатов он возвращает по одной строке на каждую комбинацию ключей. Они дают одинаковый результат только тогда, когда набор выражений в SELECT точно совпадает с ключами группировки.

Исторический контекст

В SQL нужны два разных механизма: устранение дублей в результате проекции и разбиение строк на группы для аналитических вычислений. DISTINCT решает первую задачу, а GROUP BY создаёт основу для агрегатов вроде COUNT, SUM и AVG.

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

Постановка проблемы

Если отчёт просто должен показать уникальные комбинации выбранных значений, использование GROUP BY может скрыть ошибку: разработчик начнёт группировать по дополнительным столбцам или применять неуместные агрегаты. Обратная ошибка также опасна: DISTINCT не позволяет получить показатели по группам и может замаскировать необходимость посчитать количество или сумму.

Особенно важен состав ключей. Группировка по большему числу столбцов, чем выведено в SELECT, способна вернуть несколько одинаковых на вид строк, тогда как DISTINCT удалил бы такие дубли.

Подробное решение

DISTINCT сравнивает полные строки результата по всем выражениям SELECT. Например, уникальность определяется всей комбинацией выбранных столбцов, а не одним из них. Если в выбранных выражениях встречается NULL, одинаковые комбинации с NULL считаются повторяющимися и сводятся к одной строке.

GROUP BY сначала формирует группы по указанным выражениям. Без агрегатов из каждой группы обычно выводится одна строка, но SQL разрешает группировать по столбцам, которые не входят в SELECT. Поэтому группировка по двум столбцам с выводом только одного может породить несколько визуально одинаковых строк.

WITH sales(category, month, amount) AS ( VALUES ('A', 1, 10), ('A', 2, 20), ('A', 1, 30) ) SELECT DISTINCT category FROM sales; SELECT category FROM sales GROUP BY category, month;

Первый запрос возвращает одну строку для категории A. Второй формирует группы по паре category и month, поэтому категория A может появиться дважды. Если нужны только уникальные значения category, корректнее явно использовать DISTINCT или группировать только по category.

При наличии агрегатов выбор обычно очевиден: GROUP BY выражает намерение получить показатель для каждой группы. DISTINCT может убрать повторяющиеся одинаковые строки после вычисления, но не заменяет группировку и не служит средством подсчёта.

Ни один из механизмов сам по себе не гарантирует порядок результата. Для стабильного порядка нужен явный ORDER BY. Оптимизатор может физически выполнить эквивалентные запросы похожим способом, но полагаться на это как на семантику нельзя.

Ситуация из практики

В отчёте нужно посчитать число уникальных клиентов по регионам, но исходная таблица содержит несколько событий одного клиента в одном регионе. Рассматривались два варианта: сразу использовать COUNT(DISTINCT customer_id), что компактно и не требует промежуточного результата, или сначала получить уникальные пары клиента и региона через DISTINCT, а затем выполнить GROUP BY по региону.

Для разового простого отчёта выбран первый вариант: он короче и непосредственно выражает требование. Для переиспользуемого набора данных выбран второй вариант: очищенные пары можно применять в нескольких расчётах, а логика устранения дублей отделена от последующей агрегации. Результат стал корректным: повторные события больше не увеличивали число клиентов.

Что кандидаты часто упускают

  1. Можно ли считать DISTINCT и GROUP BY полностью взаимозаменяемыми без агрегатов?

Нет. Они эквивалентны только при совпадении набора выражений SELECT и ключей группировки. Если GROUP BY использует дополнительные столбцы, результат может содержать одинаковые на вид строки; если SELECT содержит выражения, их нужно сопоставлять с тем, по чему реально формируются группы.

  1. Как ведут себя DISTINCT и GROUP BY при наличии NULL?

Оба механизма сводят строки с одинаковой комбинацией значений NULL к одной группе или одной результирующей строке. Это не означает, что NULL становится обычным значением для сравнений: правила трехзначной логики в условиях WHERE сохраняются. Различие проявляется не в самом схлопывании NULL, а в возможностях GROUP BY применять агрегаты и HAVING.

  1. Что произойдёт, если к запросу с оконной функцией добавить DISTINCT?

DISTINCT применяется к результату, где оконная функция уже вычислена на строках своего входного набора. Поэтому строки, одинаковые до оконного вычисления, могут стать различными из-за значения оконной функции и не схлопнуться. Если требуется сначала сократить набор, дедупликацию нужно выполнить во вложенном запросе или CTE, а оконную функцию применить уровнем выше.