Files
airflow-greenplum/sql/validate/ods_no_dup_bk.sql
T
ddadmin a51e2a6243 feat(validate): добавлен валидационный DAG bookings_validate
- Зачем:
  - студент реализует 9 объектов DWH без автоматической обратной связи;
  - ошибки (NULL в PK, дубли BK, сломанный SCD2) обнаруживались только на слое DM
    через 2–3 слоя, где отладка многократно сложнее.
- Что:
  - создан DAG bookings_validate.py — три параллельных TaskGroup (validate_ods,
    validate_dds, validate_dm), schedule=None (только ручной trigger);
  - создано 17 SQL-скриптов в sql/validate/: ODS BK coverage vs STG-батч,
    согласованность _load_id между ods.airplanes и ods.seats, дубли BK, NULL в PK,
    покрытие ODS→DDS для измерений (dim_routes требует открытой версии valid_to IS NULL),
    SCD2 активный тест backup→mutate→student_load→check(5 инвариантов)→restore
    (trigger_rule=all_done), проверка «дыр» между SCD2-версиями,
    exists-чеки для 4 DM-витрин;
  - добавлен класс TestBookingsValidate (5 smoke-тестов) в tests/test_dags_smoke.py.
- Проверка:
  - make test — 4 passed, 14 skipped (DAG-тесты скипаются без Airflow, норма);
  - ручной trigger bookings_validate в Airflow UI на solution-ветке — все таски зелёные.
2026-03-12 12:12:44 +03:00

56 lines
2.8 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- Проверка ODS-инвариантов:
-- 1. Нет дублей по BK в ods.airplanes и ods.seats.
-- 2. Обе таблицы загружены из одного согласованного батча (_load_id совпадают).
--
-- Почему важна согласованность батча:
-- ods.airplanes и ods.seats — части одного snapshot'а (TRUNCATE+INSERT из одного STG-батча).
-- Если они собраны из разных батчей (например, airplanes перезагрузили, а seats — нет),
-- JOIN между ними даст неконсистентный срез и невалидный total_seats в dim_airplanes.
DO $$
DECLARE
v_dup_airplanes BIGINT;
v_dup_seats BIGINT;
v_load_id_airplanes TEXT;
v_load_id_seats TEXT;
BEGIN
-- airplanes: BK = airplane_code
SELECT COUNT(*) INTO v_dup_airplanes
FROM (
SELECT airplane_code FROM ods.airplanes
GROUP BY airplane_code HAVING COUNT(*) > 1
) AS d;
IF v_dup_airplanes > 0 THEN
RAISE EXCEPTION 'FAILED: ods.airplanes содержит % дублирующихся airplane_code. Проверьте, что load начинается с TRUNCATE.', v_dup_airplanes;
END IF;
-- seats: BK = (airplane_code, seat_no)
SELECT COUNT(*) INTO v_dup_seats
FROM (
SELECT airplane_code, seat_no FROM ods.seats
GROUP BY airplane_code, seat_no HAVING COUNT(*) > 1
) AS d;
IF v_dup_seats > 0 THEN
RAISE EXCEPTION 'FAILED: ods.seats содержит % дублирующихся (airplane_code, seat_no). Проверьте, что load начинается с TRUNCATE.', v_dup_seats;
END IF;
-- Согласованность батча: обе таблицы должны быть загружены из одного _load_id
SELECT DISTINCT _load_id INTO v_load_id_airplanes FROM ods.airplanes;
SELECT DISTINCT _load_id INTO v_load_id_seats FROM ods.seats;
IF v_load_id_airplanes IS NOT NULL
AND v_load_id_seats IS NOT NULL
AND v_load_id_airplanes <> v_load_id_seats
THEN
RAISE EXCEPTION E'FAILED: ods.airplanes и ods.seats загружены из разных батчей.\n'
' airplanes._load_id = %\n'
' seats._load_id = %\n'
'ODS — единый snapshot: обе таблицы должны содержать один и тот же _load_id.\n'
'Запустите оба load-скрипта (airplanes_load.sql и seats_load.sql) в одном прогоне.',
v_load_id_airplanes, v_load_id_seats;
END IF;
RAISE NOTICE 'PASSED: Нет дублей по BK в ods.airplanes и ods.seats; батч согласован (%)', v_load_id_airplanes;
END $$;