Отчёты по уникальным пользователям необходимы для аналитики и статистики. Однако в реальных данных часто встречаются дубликаты аккаунтов, NULL-значения (например, неуказанный email), и требуется учитывать различные критерии (например, уникальность по имени, почте, ip, а иногда их комбинации).
Типичная ошибка — считать COUNT(DISTINCT user_id), не учитывая, что в нужных столбцах есть NULL или неочевидные дубликаты (например, один человек с разными email, или несколько строк с одним user_id и разным статусом). Сложные запросы с GROUP BY могут давать неверный результат, если не продумана логика уникальности.
Важно объединять конструкции DISTINCT, GROUP BY и фильтрацию NULL. Иногда требуется предварительно подготовить данные в CTE или вложенном подзапросе, сгруппировав по нужному множеству признаков.
Пример кода:
-- Подсчет уникальных пользователей по email и ip, игнорируя NULL SELECT COUNT(*) AS unique_users FROM ( SELECT DISTINCT email, ip_address FROM users WHERE email IS NOT NULL AND ip_address IS NOT NULL ) u;
Ключевые особенности:
COUNT(DISTINCT ...) учитывает строки с NULL?
Нет: если хотя бы один из столбцов в списке DISTINCT имеет значение NULL, такая комбинация считается отдельной уникальной (NULL не равен NULL по SQL-стандарту). Обычно удобнее сперва убрать NULL фильтрацией.
Можно ли сравнивать NULL с NULL через DISTINCT?
В SQL каждая пара NULL-значений считается разной, поэтому каждая строка с NULL в любом из колонок будет считаться отдельной. Нужно применять фильтрацию через IS NOT NULL.
GROUP BY всегда даёт такой же результат, как DISTINCT?
Нет: GROUP BY создаёт по одной строке на каждую неповторяющуюся комбинацию значений, а DISTINCT — просто удаляет дубликаты. В ряде случаев результат разный, особенно если применяется агрегация.
Бизнес-аналитик строит отчёт по уникальным клиентам через COUNT(DISTINCT user_id), а user_id в реальности может быть NULL или дублироваться (например, временные аккаунты). Фактическое число пользователей получается больше реального — искажённые показатели в отчёте.
Плюсы:
Минусы:
Аналитик предварительно очищает данные, отфильтровывает NULL и явные дубликаты в подзапросах, а также использует SET-операции для сложных критериев уникальности.
Плюсы:
Минусы: