Неверный отчёт не всегда выглядит неправдоподобно. Общая сумма может совпадать, а распределение по категориям быть ошибочным. Часто причина не в синтаксисе SQL, а в неописанном зерне, кратности связи или выборе исторической версии.
Размножение фактов
Одна продажа содержит две позиции, а у товара есть три тега. Если соединить позиции с тегами и затем сложить выручку, каждая позиция товара с тремя тегами появится трижды. JOIN выполнился правильно: модель допустила связь один-ко-многим. Ошибка возникает в трактовке полученного зерна.
Решение зависит от вопроса. Можно считать число продаж, где встречается тег, но один чек может войти в несколько категорий. Если нужно распределить сумму между тегами так, чтобы общий итог сохранился, понадобится явное правило весов. SUM(DISTINCT сумма) не является общим исправлением: две независимые позиции с одинаковой суммой тогда ошибочно сольются.
Проверяйте число совпадений справа на один факт и сверяйте меры до и после JOIN. Делайте это по разрезам, а не только по общему итогу.
Текущий справочник вместо истории
Товар перевели в другую категорию. Отчёт «по категории на дату продажи» должен выбрать версию справочника, действовавшую в момент продажи. Соединение с текущей категорией перенесёт старые продажи в новую группу. Общая выручка при этом останется прежней, что делает ошибку менее заметной.
Отчёт по нынешней структуре категорий тоже может быть полезен. Нужно назвать его именно так и не подменять исторический вопрос. Для исторического разреза сохраняют версии атрибутов и проверяют, что у каждого факта нашлась ровно одна подходящая версия.
Среднее средних и сложение остатков
При объединении групп среднюю рассчитывают из общей суммы и количества. Дневные количества уникальных пользователей требуют объединения множеств: один человек может присутствовать в нескольких днях.
Остаток на конец дня — состояние. Складывать остатки по разным дням как «общее количество» нельзя. Для него выбирают конкретный момент или рассчитывают среднее состояние за период по определённому правилу.
Смешение времён и зерна
Дата создания заявки, дата закрытия и дата загрузки в DWH отвечают на разные вопросы. Если группировать закрытые заявки по дате создания, получится распределение старта работы, а не числа закрытий по дням. Обе колонки могут быть корректными; важно выбрать нужную роль даты.
Детальная таблица событий и дневной агрегат тех же событий не являются независимыми источниками фактов. Их суммы нельзя складывать вместе. Перед запросом запишите зерно каждого набора и объясните, что означает одна строка результата.
Потеря строк при INNER JOIN
Факт пришёл раньше справочника. INNER JOIN исключит его из отчёта без явной ошибки выполнения. Это может быть допустимо только если правило публикации требует полностью сопоставленные данные. Иначе используйте отложенную обработку, неизвестный элемент измерения или отдельный список несопоставленных фактов.
LEFT JOIN сохраняет строки без справочника, но не исправляет ошибочные ключи и не защищает от нескольких совпадений. После соединения проверяйте отсутствующие связи отдельно.
Проверка модели перед публикацией
Для каждой важной таблицы достаточно короткого паспорта: зерно, ключ, время события, источник, правило обновления, меры и разрешённые разрезы. Для каждого JOIN — ожидаемая кратность. Для исторического атрибута — способ выбора версии. Затем сверяйте число фактов и контрольные меры на нескольких уровнях детализации.
Если проверка общего итога проходит, а распределение по категориям меняется после JOIN, исследуйте историю и соответствие ключей. Совпадение одной суммы не доказывает правильность всей модели.