Skip to content
PПромтбук
RUEN
07Пайплайны

Data quality checks: schema, nullness, freshness, business rules

Где запускать (input/output/cross), fail vs warn, метрики для дашборда, как не превратить тесты в боль.

Действуй как data engineer, который ловил молчаливые drift'ы в продакшене. Спроектируй DQ-стратегию для: {{pipeline}}. Стек: {{stack}}.

Зачем формальные DQ-чеки

Без них баги ловятся через "цифра в отчёте странная". Цикл: аналитик заметил → инженер 3 дня ищет → находит, что неделю назад дубль. Формальные чеки сокращают это до "падение пайплайна → исправили за час".

Типы checks

Schema

Структура данных не уплыла:

  • Колонки присутствуют, типы те же
  • Новые колонки — warn, удаление существующих — fail
  • Constraint'ы (NOT NULL, length) совпадают

Запускать: до transform'а в staging.

Nullness

  • NOT NULL на ключи, FK, бизнес-критичные поля
  • % NULL в опциональных полях не должен внезапно вырасти (drift)
SELECT
  COUNT(*) FILTER (WHERE user_id IS NULL) AS null_user_id,
  COUNT(*) AS total
FROM stg_orders;
-- fail if null_user_id > 0

Uniqueness

PK и сурогаты должны быть уникальны. Особенно после merge'ей — дубли любят появляться там.

SELECT order_key, COUNT(*) FROM fct_orders GROUP BY 1 HAVING COUNT(*) > 1;

Referential integrity

Каждый FK в факте находит свою dim. Иначе in BI получишь NULL'ы и непонятно почему.

SELECT f.customer_key FROM fct_orders f
LEFT JOIN dim_customer d USING (customer_key)
WHERE d.customer_key IS NULL;

Freshness

max(loaded_at) против SLA. Каждый mart знает, как часто должен обновляться.

SELECT EXTRACT(EPOCH FROM (NOW() - MAX(loaded_at)))/3600 AS hours_stale
FROM marts.fct_orders;
-- warn > 2h, fail > 4h

Business rules

То, что не выводится из схемы:

  • "сумма выручки w/w не падает > 30%"
  • "AOV (avg order value) в коридоре $20-$500"
  • "% refunds < 5%"
  • "никаких заказов с future ship_date"

Это самые ценные тесты — ловят bug'и, которые schema-валидация пропустит.

Где запускать

| Где | Что проверяем | Действие при fail | | Input (raw) | Schema, freshness источника | Останавливаем pipeline, алерт data team | | Output (mart) | Uniqueness, business rules | Останавливаем downstream, алерт consumers | | Cross-table | Referential, реконсиляция | Алерт, но не блокируем (часто eventual consistency) |

Fail vs warn

| Уровень | Когда | Что делает | | FAIL | Данные сломаны, использовать нельзя | Прерывает pipeline, dashboard "stale", алерт | | WARN | Подозрительно, но не критично | Логирует в метрику, не останавливает | | INFO | Drift-мониторинг | Только в дашборд тренда |

Каждый чек должен иметь явный severity, иначе все становятся "критичны" и alert fatigue.

Дашборд для drift

Метрики, которые стоит трекать как time-series:

  • row count per table per day
  • % null по бизнес-критичным колонкам
  • distinct values в low-cardinality колонках (изменения = новые статусы появились)
  • p50/p95 числовых полей (AOV, latency, etc.)
  • freshness lag

Алерты на резкие изменения (>3σ от 30-дневной нормы), не на абсолютные значения.

Вывод

## Чек-лист по таблицам
| Таблица | Schema | Nullness | Unique | Ref | Freshness | Business |

## Severity matrix
| Чек | FAIL/WARN | Канал алерта | Owner |

## Бизнес-правила
| Правило | SQL | Severity | Owner |

## Drift-дашборд
| Метрика | Источник | Тренд | Алерт-порог |

Anti-patterns

  • ❌ Всё на FAIL — приходит 50 алертов в день, команда выключает уведомления
  • ❌ Только schema-тесты — пропускают логические баги ("все суммы умножились на 100")
  • ❌ Тесты только в проде — должны бежать в CI на staging, иначе ловишь после релиза
  • ❌ Бизнес-правила в одном гигантском SQL — невозможно понять, что именно сломалось
  • ❌ Нет owner'а у чека — никто не реагирует на алерт, через неделю все игнорируют
К подразделу «Пайплайны»
Похожие промты