Качество данных: как настроить проверки и мониторинг
Проверка, которая один раз нашла пустые значения, ещё не защищает отчёт завтра. Разбираем на примере PostgreSQL 16, как сочетать ограничения, SQL-проверки и мониторинг, чтобы ловить ошибки до того, как на них наткнётся пользователь.

В таблице заказов вчера не было пустых сумм, а сегодня из-за сбоя загрузки появились NULL — и отчёт по выручке тихо занизил результат. Одной проверки при создании таблицы недостаточно: часть ошибок можно остановить ограничениями PostgreSQL, часть — найти SQL-проверками, а изменения во времени заметить только мониторингом. Ниже — практическая схема для PostgreSQL 16 и таблицы аналитических данных.
Проверка данных — не то же самое, что мониторинг
Проверка отвечает на вопрос «есть ли нарушение сейчас?»: например, нашлись ли заказы с отрицательной суммой. Мониторинг отвечает на другой вопрос: «как меняется состояние данных и кто узнает о проблеме?». Он хранит результаты проверок или показатели по времени и сообщает об отклонении.
Эти задачи лучше не смешивать. SQL-запрос, который возвращает ноль строк, — полезная проверка, но сам по себе он не отправляет уведомление и не показывает, что вчера ошибок было ноль, а сегодня их стало двести. И наоборот: график количества строк не объяснит, почему сумма заказа отрицательная.
Проверка без владельца и реакции — это не защита данных, а запрос, который кто-то должен не забыть запустить.
Термин. Инвариант данных — правило, которое должно оставаться верным для каждой записи или набора записей. Например: сумма заказа неотрицательна, а идентификатор заказа уникален.
Три уровня защиты в PostgreSQL 16
1. Ограничения базы для правил, которые нельзя нарушать
Если значение недопустимо для всех потребителей таблицы, закрепите правило в самой базе. Ограничение будет действовать независимо от того, загрузили данные через приложение, скрипт или ETL-процесс. Для новых таблиц типичные инструменты — NOT NULL, UNIQUE, FOREIGN KEY и CHECK.
Например, это ограничение допускает только неотрицательную сумму:
ALTER TABLE public.orders
ADD CONSTRAINT orders_amount_nonnegative
CHECK (amount >= 0);
Важно: CHECK с выражением amount >= 0 не заменяет NOT NULL. В SQL результат сравнения с NULL не равен false, поэтому отдельное требование к заполненности задают отдельно. Ограничение UNIQUE, в свою очередь, защищает от повторов по ключу, но может быть дорогим при добавлении на большую таблицу и потребовать предварительной проверки данных.
2. SQL-проверки для правил, которые проще выразить запросом
Не каждое условие удобно задавать ограничением. Проверить необычные статусы, резкое падение числа загрузок или дубликаты составного ключа проще запросом, который возвращает проблемные строки или количество нарушений. Такие запросы запускают после загрузки данных — например, планировщиком задач или этапом конвейера.
Проверка отрицательных и пустых сумм:
SELECT id, amount
FROM public.orders
WHERE amount IS NULL OR amount < 0;
Проверка повторов идентификатора:
SELECT id, COUNT(*) AS copies
FROM public.orders
GROUP BY id
HAVING COUNT(*) > 1;
Для списков допустимых значений не забывайте отдельно проверять NULL: условие status NOT IN (...) не выберет строки, где status пустой. Ещё одна ловушка — проверять только итоговую витрину. Если ошибка появилась на этапе импорта, её полезнее поймать у границы загрузки, пока она не разошлась по зависимым таблицам.
3. Мониторинг для динамики и своевременного сигнала
Некоторые сбои не нарушают формальные правила. Таблица может содержать только допустимые значения, но обновляться с задержкой; число заказов может резко упасть, хотя ни одна запись не сломана. Для этого отслеживают свежесть данных, объём загрузки и число нарушений проверок.
Простейший запрос для оценки свежести:
SELECT now() - MAX(created_at) AS data_age
FROM public.orders;
Если таблица пуста, MAX вернёт NULL. Это стоит считать отдельным состоянием, а не нормальной свежестью. Кроме того, created_at может означать время создания заказа, а не время его поступления в хранилище: запоздавшие события тогда исказят оценку. Для контроля доставки полезно измерять именно время загрузки, если оно хранится в таблице.
Порог для тревоги нельзя назначить универсально. Для ежедневного отчёта задержка на час может быть приемлемой, а для оперативного мониторинга — уже критичной. Зафиксируйте ожидаемую частоту обновления, допустимое опоздание, получателя сигнала и действие при тревоге. Без этого мониторинг будет либо шуметь, либо молчать до жалобы пользователя.
Какой подход выбрать
| Подход | Что хорошо ловит | Цена и ограничение |
|---|---|---|
| Ограничения PostgreSQL | Недопустимые значения, пропуски, нарушения ссылок и уникальности | Правила живут рядом с данными и не зависят от приложения. Добавление ограничений на крупную заполненную таблицу требует планирования; часть межтабличных и временных условий ими не выразить. |
| SQL-проверки после загрузки | Дубликаты, необычные значения, нарушения бизнес-правил и качества конкретной витрины | Логику легко читать и менять, но запрос должен запускаться регулярно. Нужны владелец, журнал результата и понятная реакция. |
| Мониторинг показателей | Задержки, пустые загрузки, падение или всплеск объёма, рост числа ошибок | Показывает изменения во времени и помогает вовремя заметить сбой. Пороги нужно привязать к режиму работы системы, иначе уведомления быстро перестанут замечать. |
На практике подходы дополняют друг друга. Жёсткие правила, которые должны соблюдаться всегда, размещайте в базе. Правила уровня витрины проверяйте запросами. Для свежести и объёма храните историю показателей и поднимайте тревогу при отклонении от согласованных ожиданий.
Как внедрить проверки без потери данных
- Выберите таблицу и владельца. Начните с таблицы, от которой зависит важный отчёт или процесс. Укажите, кто разбирает сигнал и кто может исправить загрузку. Проверка без ответственного быстро превращается в забытый пункт расписания.
- Запишите правила на языке данных. Для каждого правила определите область, исключения и действие при нарушении. Например, «сумма неотрицательна» — правило базы, а «ежедневная загрузка должна завершиться до начала отчёта» — правило мониторинга. Проверка готова к реализации, когда два человека одинаково понимают, что считать ошибкой.
- Проверьте текущее состояние до включения ограничений. Выполните поиск нарушений на существующих данных и оцените, сколько строк затронуто. Если добавить ограничение поверх уже испорченных данных, миграция может завершиться ошибкой; сначала решите, исправлять, исключать или разбирать строки отдельно.
- Настройте запуск и сохраняйте результаты. Запускайте SQL-проверки после завершения соответствующей загрузки, а не по произвольному расписанию, которое может совпасть с незавершённым обновлением. Сохраняйте время запуска, имя проверки, количество нарушений и статус. Проверка считается работающей, когда вы можете увидеть последний результат и понять, кто получил сигнал.
- Проверьте реакцию на сбой. На тестовой среде создайте нарушение или смоделируйте пропуск загрузки. Убедитесь, что проверка обнаруживает проблему, уведомление доходит до ответственного, а корректный повторный запуск не оставляет ложную тревогу. Готовность подтверждена, если команда может пройти этот сценарий от обнаружения до восстановления.
Когда проверки начинают ломаться
Сработало много ложных тревог. Часто порог не учитывает расписание, выходные или задержку источника. Привяжите ожидание к реальному графику загрузки и различайте состояние «данные опаздывают» и состояние «проверка не запустилась».
Проверка зелёная, а отчёт неверный. Условия могли описывать только техническую допустимость строк, а не смысл показателя. Для выручки, например, нужно явно определить, какие статусы и периоды входят в расчёт, и сверять результат с контрольным источником.
После изменения схемы тесты перестали работать. Запросы могут ссылаться на переименованные поля или ожидать старое значение. Включите проверки в процесс изменения схемы и прогоняйте их на тестовых данных до публикации новой версии таблицы.
Сигнал приходит, но никто не понимает, что делать. Сообщение должно называть таблицу, правило, время проверки, число найденных строк и ссылку на место разбора. Не отправляйте все строки с персональными или чувствительными данными в общий канал: достаточно безопасной сводки и ограниченного доступа к деталям.
Большая проверка замедляет загрузку. Запрос с группировкой по всей таблице может быть дорогим, особенно если он запускается часто. Сначала измерьте длительность, план выполнения и влияние на параллельные запросы; затем решайте, ограничивать ли проверку свежим периодом, пересматривать ли индекс или запускать её реже. Не считайте выборку доказательством отсутствия ошибок во всей таблице.
Что измерять в рабочей системе
До оптимизации зафиксируйте фактическое поведение на своём объёме данных: время каждой проверки, число обработанных строк, количество нарушений, задержку от ожидаемого обновления и долю успешных загрузок. Сравнивайте одинаковые временные окна и одинаковые условия запуска. Без таких замеров нельзя честно утверждать, что проверка ускорилась или стала надёжнее.
Обязательно проверьте граничные случаи: пустую таблицу, повторную загрузку одного периода, опоздавшие события, NULL в ключевых полях и частично завершённый импорт. Подход ломается там, где правило не учитывает реальный процесс: например, требование уникальности может быть неверным, если источник легально присылает несколько версий одной записи.
Что делать дальше
Выберите одну важную таблицу, заведите для неё два-три правила и назначьте владельца каждого сигнала. Сначала наблюдайте результаты без автоматического отклонения загрузки: так вы увидите качество исторических данных и настроите пороги. Затем закрепите критические инварианты ограничениями PostgreSQL, а в проде следите за свежестью, объёмом загрузки, длительностью проверок и числом ложных тревог. Инвалидация ошибочных записей и их исправление — отдельная тема; здесь речь только о проверках и обнаружении проблем.
Логотип и фирменный стиль: Три концепции разного характера, палитра с обоснованием, проверка на читаемость в малом размере и в один цвет.
Спросить за 60 ₽Источники: PostgreSQL 16: ограничения целостности данных, PostgreSQL 16: статистика и мониторинг
Частые вопросы
Нужно ли проверять каждую строку?
Можно ли проверять качество только ограничениями PostgreSQL?
Что делать, если ограничение не добавляется к старой таблице?
Подойдут ли примеры для другой версии PostgreSQL?
Что делать, если проверка ничего не нашла, а данные всё равно неверные?
Какие показатели важнее всего мониторить в проде?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.