# Классы DQ-проверок DQ-проверка должна отвечать на конкретный вопрос. В этом проекте есть три класса. Один `*_dq.sql` может содержать проверки нескольких классов. ## 1. Универсальные структурные проверки Они проверяют форму таблицы: обязательные ключи заполнены, ключ уникален, а таблица не пуста там, где по контракту ожидаются строки. Так можно быстро поймать сломанное зерно или незавершённую загрузку без знания бизнес-смысла данных. Реальные примеры: - [`sql/dds/dim_airports_dq.sql`](../../sql/dds/dim_airports_dq.sql) требует непустую таблицу, проверяет уникальность `airport_sk` и `airport_bk`, а также обязательные поля. - [`sql/ods/bookings_dq.sql`](../../sql/ods/bookings_dq.sql) ищет дубли `book_ref` и `NULL` или пустые значения в обязательных полях. Пустой инкрементальный батч здесь допустим. - [`sql/ods/tickets_dq.sql`](../../sql/ods/tickets_dq.sql) проверяет уникальность `ticket_no`, обязательные поля и отсутствие билетов без `bookings.book_ref`. ## 2. Проверки бизнес-правил Они проверяют ограничения предметной области, которых не видно из структуры таблицы. Такая проверка нужна, чтобы заметить правдоподобную по типам, но невозможную по смыслу строку. В [`sql/dm/sales_report_dq.sql`](../../sql/dm/sales_report_dq.sql) для дат текущего запуска проверяется, что `tickets_sold >= passengers_boarded`, `boarding_rate` лежит между `0` и `1`, а `total_revenue` неотрицательна. ## 3. Межслойные сверки Они отвечают на вопрос: согласован ли слой N+1 со слоем N для нужного батча? Сверка находит потерянные или лишние строки на границе загрузки. Реальные примеры используют разные способы сверки: - [`sql/ods/airports_dq.sql`](../../sql/ods/airports_dq.sql) сравнивает множества `airport_code`: ищет пропущенные и лишние ключи относительно текущего STG-батча. Это сверка ключей, а не простое сравнение числа строк. - [`sql/stg/airplanes_dq.sql`](../../sql/stg/airplanes_dq.sql) сравнивает число строк во внешней `stg.airplanes_ext` и в текущем батче `stg.airplanes`. - [`sql/dds/fact_flight_sales_dq.sql`](../../sql/dds/fact_flight_sales_dq.sql) требует равенства числа строк факта и `ods.segments`, потому что зерно факта — один сегмент билета. ## `NULL` — не всегда ошибка В [`fact_flight_sales_load.sql`](../../sql/dds/fact_flight_sales_load.sql) измерения присоединяются через `LEFT JOIN`. Факт может прийти раньше измерения, поэтому `route_sk`, `airplane_sk` или `passenger_sk` временно бывают `NULL`. На ветке `main` это также ожидаемо, пока студент не реализовал эти измерения. Такой `NULL` сохраняет строку факта и не должен сам по себе останавливать пайплайн. `seat_no` тоже может быть `NULL`: у билета уже есть сегмент, но записи `boarding_passes` ещё нет. Обязательный ключ, напротив, должен быть заполнен. Например, `NULL` в `airport_sk` измерения или в `(ticket_no, flight_id)` факта нарушает контракт таблицы. Архитектурное решение о том, где живут DQ-проверки, зафиксировано в разделе [«Ключевые договорённости»](../design/db_schema.md#ключевые-договорённости).