Валидация данных в аналитике

Зачем валидировать данные

Плохие данные → неправильные выводы → неправильные решения → потерянный бизнес.

Типичные последствия:

  • «Revenue вырос на 20%» → на самом деле ETL добавил дубликаты.
  • «DAU упал» → сломался трекер push-ей.
  • «A/B-тест значим» → SRM — сплит нечестный.

Шесть направлений валидации

1. Completeness (полнота)

Все ли данные пришли? Нет ли пропусков?

-- Доля NULL в ключевой колонке
SELECT
    COUNT(*) FILTER (WHERE email IS NULL) * 100.0 / COUNT(*) AS null_pct
FROM users;
  • OK: стабильный % NULL.
  • Alert: скачок NULL вверх.

2. Uniqueness (уникальность)

Дубликаты где их не должно быть?

-- Ожидаем уникальных email
SELECT email, COUNT(*) FROM users
GROUP BY email HAVING COUNT(*) > 1;

3. Validity (корректность)

Значения в допустимом диапазоне?

-- Возраст должен быть 0–120
SELECT * FROM users WHERE age < 0 OR age > 120;

-- Email должен соответствовать паттерну
SELECT * FROM users WHERE email NOT ~ '^[^@]+@[^@]+\.[^@]+$';

-- Статус только из списка
SELECT * FROM orders WHERE status NOT IN ('paid', 'pending', 'refunded');

4. Consistency (консистентность)

Данные в разных местах согласованы?

-- Заказы без соответствующих пользователей (orphan records)
SELECT * FROM orders o
WHERE NOT EXISTS (SELECT 1 FROM users WHERE user_id = o.user_id);

-- Сверка итогов
SELECT
    (SELECT SUM(amount) FROM orders WHERE status = 'paid') AS db_total,
    (SELECT SUM(amount) FROM mart_revenue_daily) AS mart_total;

5. Timeliness (своевременность)

Данные свежие?

-- Последняя запись не старше 1 часа
SELECT MAX(created_at) AS last_event FROM events;

-- Алерт если отставание > 2 часов

6. Accuracy (точность)

Соответствуют ли данные реальности?

Самое сложное — требует сверки с внешним источником:

  • Revenue в БД vs deposits в банке.
  • Количество пользователей в dashboard vs в CRM.

Если хочется сразу закрепить тему на практике — открой тренажёр в Telegram. 10 минут в день — и синтаксис в пальцах.

Автоматические проверки

dbt tests

models:
  - name: fct_orders
    columns:
      - name: order_id
        tests:
          - unique
          - not_null
      - name: amount
        tests:
          - not_null
          - dbt_utils.accepted_range:
              min_value: 0

Great Expectations

Python library для data validation:

import great_expectations as ge

df_ge = ge.from_pandas(df)
df_ge.expect_column_values_to_not_be_null('user_id')
df_ge.expect_column_values_to_be_between('age', 0, 120)
df_ge.expect_column_values_to_be_unique('email')

Airflow sensors и checks

from airflow.operators.python import PythonOperator

def check_data_quality():
    hook = PostgresHook()
    null_pct = hook.get_first('...')[0]
    if null_pct > 0.1:
        raise ValueError(f'Too many nulls: {null_pct}')

check = PythonOperator(
    task_id='data_quality_check',
    python_callable=check_data_quality
)

Практические проверки по таблицам

users

-- Дубликаты по email
SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1;

-- Невалидные email
SELECT * FROM users WHERE email NOT ~ '^[^@]+@[^@]+\\.[^@]+$';

-- Registered_at из будущего
SELECT * FROM users WHERE registered_at > NOW();

orders

-- Amount отрицательный?
SELECT * FROM orders WHERE amount < 0;

-- User_id не существует
SELECT * FROM orders o
WHERE NOT EXISTS (SELECT 1 FROM users WHERE user_id = o.user_id);

-- Status не из списка
SELECT DISTINCT status FROM orders
WHERE status NOT IN ('paid', 'pending', 'refunded', 'cancelled');

events

-- Event_time старше 1 года
SELECT * FROM events WHERE event_time < NOW() - INTERVAL '1 year';

-- Пиковые bots (много событий за секунду)
SELECT user_id, COUNT(*) FROM events
WHERE event_time > NOW() - INTERVAL '1 minute'
GROUP BY user_id HAVING COUNT(*) > 100;

Мониторинг и алерты

Что мониторить

  • Количество записей в ключевых таблицах (растёт/падает?).
  • % NULL в важных колонках.
  • Последний timestamp (lag).
  • Ошибки ETL в логах.

Где настраивать

  • Airflow: DAG с ежедневной проверкой + alert в Slack.
  • dbt: dbt test в CI/CD, fail pipeline при проблемах.
  • Great Expectations: notebook с checks, выполняется по расписанию.
  • Grafana/Datadog: dashboard с data-quality метриками.

Пример алерта

def alert_null_spike():
    today = get_null_pct('email')
    yesterday = get_null_pct_yesterday('email')
    if today > yesterday * 2:
        slack_notify(f'NULL spike in users.email: {today}%')

Чтобы не только читать теорию, но и решать реальные задачи — загляните в бот Карьерника. Там по каждой теме подборка вопросов с разборами.

Data Contract

Современная практика — contract между producer и consumer данных:

  • Formal schema (колонки, типы).
  • SLA (latency, freshness).
  • Тесты (validity, uniqueness).

Инструменты: dbt Contract, Soda Core, Monte Carlo.

Читайте также

FAQ

Кто отвечает за валидацию — аналитик или DE?

Совместно. DE — за pipeline и infrastructure. Аналитик — за business-логику и корректность метрик.

Как часто запускать проверки?

Продакшн-критичные — ежедневно. Остальные — еженедельно. Для real-time ETL — при каждом batch.

dbt или Great Expectations?

dbt — если есть dbt pipeline. GE — независимо. Совместимы.

Что важнее — uniqueness или completeness?

Зависит от контекста. PK duplicate может сломать JOIN → большая проблема. NULL в описательной колонке — меньшая.