В отчёте сумма числа уникальных клиентов по регионам оказалась больше общего числа уникальных клиентов. Как...

В отчёте сумма числа уникальных клиентов по регионам оказалась больше общего числа уникальных клиентов. Какой принцип агрегации объясняет расхождение?

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

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

COUNT(DISTINCT ...) считается отдельно внутри каждой группы, поэтому один клиент, присутствующий в нескольких регионах, будет посчитан в каждой из них. Общее число уникальных клиентов считается по объединению всех групп и не равно сумме групповых значений, если множества клиентов пересекаются.

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

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

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

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

Предположим, один клиент совершал покупки в двух регионах. В отчёте по регионам он должен учитываться один раз в каждом регионе, потому что это два независимых групповых результата.

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

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

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

WITH sales(customer_id, region) AS ( VALUES (1, 'Север'), (1, 'Юг'), (2, 'Север'), (3, 'Юг') ) SELECT region, COUNT(DISTINCT customer_id) AS unique_customers, (SELECT COUNT(DISTINCT customer_id) FROM sales) AS overall_unique FROM sales GROUP BY region;

В результате для каждого региона будет по два уникальных клиента, но общий показатель составит три: клиент с идентификатором 1 присутствует в обоих регионах. Поэтому сумма групповых значений равна четырём и не совпадает с общим итогом.

Нельзя исправить проблему простым добавлением DISTINCT к уже агрегированным значениям: после группировки информация о том, какие именно клиенты вошли в каждую группу, потеряна. Для точного общего итога нужно выполнять COUNT(DISTINCT customer_id) на исходном объединённом наборе или хранить промежуточные множества идентификаторов.

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

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

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

Команда сравнивала клиентскую активность по каналам продаж. В канале сайта было 100 тысяч уникальных клиентов, в мобильном приложении — 80 тысяч, а сумма показывала 180 тысяч. При этом общий точный подсчёт дал 140 тысяч клиентов, потому что 40 тысяч пользователей использовали оба канала.

Рассматривались два варианта. Первый — складывать показатели каналов: это быстро и просто, но результат неверен при пересечении аудиторий. Второй — считать уникальных клиентов по исходным событиям сразу для нужного общего среза: результат точный, но требует больше ресурсов.

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

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

1. Вопрос: Когда сумму уникальных клиентов по группам всё же можно использовать как общий итог?

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

Важно проверить не только бизнес-правило, но и данные. Дубли, изменившийся атрибут клиента или связь «клиент — несколько регионов» нарушат непересекаемость, даже если столбец формально называется регионом.

2. Можно ли получить точный общий показатель, сложив уникальные значения по группам и вычтя дубли?

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

На практике обычно надёжнее повторно выполнить COUNT(DISTINCT ...) на исходных строках или построить отдельный набор уникальных пар «клиент — нужный общий срез». Ручная корректировка подходит только при чётко контролируемом количестве групп и доступных данных о пересечениях.

3. Почему предварительное удаление дублей не всегда делает групповые метрики аддитивными?

Если удалить дубли только по паре «клиент — регион», это уберёт повторные покупки клиента внутри одного региона, но не устранит его присутствие в нескольких регионах. Клиент останется отдельным элементом каждого регионального множества.

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