КПКейсПрактика
Все материалы
Продуктовая аналитикачеклистсредний

SQL-проверки качества данных: дубли, пропуски и скачки метрик

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

КПКейсПрактика22 июля 2026 г.11 мин
Два аналитика проверяют поток данных с лупой и отделяют чистые строки от дубликатов.

Самый опасный аналитический отчёт — не пустой, а уверенный и неправильный. Дашборд может показать падение DAU из-за задержки загрузки, рост выручки из-за дублей платежей или хороший retention из-за неверной даты старта. Несколько коротких SQL-проверок до расчёта помогают отделить продуктовый сигнал от поломки данных.

Коротко

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

  • Проверь размер таблицы и период загрузки.
  • Найди NULL в ключевых полях и неожиданные пустые значения.
  • Проверь дубли на уровне бизнес-события.
  • Найди события без пользователя и платежи без связанной сущности.
  • Сравни дневной объём с предыдущими днями и ожидаемым диапазоном.

Сначала зафиксируй контракт данных

SQL-проверка имеет смысл только относительно ожидаемого правила. Для `users` одна строка — один пользователь, `user_id` уникален и заполнен. Для `events` одна строка — одно событие, user_id может быть анонимным только по согласованному правилу. Для payments сумма не должна быть отрицательной без отдельного типа возврата.

Минимальный контракт учебных таблиц
ТаблицаОжидаемое правилоЧто проверяем
usersодин user_id на строкудубли и пропущенный ключ
eventsодно событие на строкудубликаты, дата и orphan user_id
paymentsодна операция на строкусумма, дата и связь с user_id

Проверка NULL и обязательных полей

Начни с полей, без которых метрика меняет смысл: user_id, event_date, event_name, amount. Не каждый NULL является ошибкой, но каждый должен иметь объяснение. Если user_id может быть анонимным, это правило нужно отразить в расчёте DAU, а не скрывать фильтром.

Проверить пропуски в событиях
select\n  count(*) as all_events,\n  count(*) filter (where user_id is null) as missing_user_id,\n  count(*) filter (where event_date is null) as missing_event_date,\n  count(*) filter (where event_name is null) as missing_event_name\nfrom events;
Не фильтруй проблему молча

Условие user_id is not null может быть частью метрики, но отдельно сохраняй число отброшенных строк. Иначе качество трекинга ухудшается, а отчёт просто продолжает выглядеть аккуратно.

Дубли: одна строка или повтор события

Дубликат нужно определять по бизнес-ключу, а не только по полному совпадению строки. Для события это может быть event_id, либо комбинация user_id, event_date, event_name и session_id. Для payments лучше иметь отдельный payment_id. Если ключа нет, сначала договорись, что именно считается повтором.

Как читать результат проверки
РезультатВозможное объяснениеСледующее действие
Повторы в один деньпользователь повторил действиепроверить session_id или event_id
Идентичные строкиповторная загрузка batchсверить ingestion job и ключ дедупликации
Скачок после релизаизменился трекингсравнить схему событий до и после релиза
Найти повторяющиеся события по простому ключу
select\n  user_id,\n  event_date,\n  event_name,\n  count(*) as rows_count\nfrom events\ngroup by user_id, event_date, event_name\nhaving count(*) > 1\norder by rows_count desc;

Orphan rows: события без пользователя

Событие с user_id, которого нет в users, может быть нормальным для ещё не загруженного справочника, а может означать сломанную связь или неверный идентификатор. LEFT JOIN помогает найти такие строки, не теряя события из исходной таблицы.

Найти события без записи в users
select\n  e.user_id,\n  count(*) as events_count\nfrom events as e\nleft join users as u using (user_id)\nwhere u.user_id is null\ngroup by e.user_id\norder by events_count desc;
  • Проверь, не различаются ли типы user_id: integer и text.
  • Сверь задержку загрузки users и events.
  • Отдельно обработай anonymous_id, если он есть в продукте.
  • Не удаляй orphan rows до понимания причины.

Скачки дневного объёма

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

Сравнить дневной объём событий
with daily as (\n  select\n    event_date,\n    count(*) as events_count\n  from events\n  group by event_date\n), daily_with_previous as (\n  select\n    event_date,\n    events_count,\n    lag(events_count) over (order by event_date) as previous_count\n  from daily\n)\nselect\n  event_date,\n  events_count,\n  previous_count,\n  events_count - previous_count as change\nfrom daily_with_previous\norder by event_date;
Аномалия — повод для проверки, не вывод

Порог вроде “в два раза больше вчера” помогает найти точку внимания, но не доказывает баг. Сверь релизы, кампании, источники событий и время загрузки.

Сверка метрики с независимым расчётом

Если дашборд показывает DAU, полезно иметь простой контрольный запрос с тем же определением active action. Сверяй не только итоговое число, но и период, таймзону, фильтры тестовых аккаунтов и количество уникальных пользователей.

Что записать рядом с контрольной метрикой
ПараметрПример
Active actioncore_action
Период2026-06-01 ≤ day < 2026-07-01
Идентичностьdistinct user_id, anonymous отдельно
ИсключенияQA, сотрудники, боты
Контрольный DAU по core action
select\n  event_date,\n  count(distinct user_id) as dau\nfrom events\nwhere event_name = 'core_action'\n  and user_id is not null\n  and event_date >= date '2026-06-01'\n  and event_date < date '2026-07-01'\ngroup by event_date\norder by event_date;

Чеклист перед публикацией отчёта

Проверка качества не должна превращаться в ритуал из десятков непонятных запросов. Начни с пяти вопросов, сохрани результат и добавь ссылку на него к дашборду или задаче аналитика.

  • Размер таблицы и свежесть последней даты ожидаемые?
  • Ключи заполнены и не дублируются?
  • Связи между таблицами не дают неожиданное число orphan rows?
  • Дневной объём и доля событий по типам не скачут без объяснения?
  • Контрольный расчёт метрики совпадает с дашбордом по определению?

Мини-практика и следующий шаг

Собери мини-аудит таблицы events: пропуски, дубли, orphan user_id и дневной объём. Затем сравни контрольный DAU с результатом из отчёта. В конце запиши, какие проверки можно запускать автоматически перед обновлением дашборда.

Продолжить чтение
Вся библиотека