docs(qa-plan): исправлены SQL-запросы по результатам прогона отладки
- Зачем:
- план содержал неверные имена полей и формулы, которые давали ошибки при выполнении.
- Что:
- `_batch_id` → `batch_id` в запросах снапшот-таблиц STG (реальное имя колонки).
- формула SCD2-маршрутов: `COUNT(DISTINCT route_no)` вместо `route_no || '-' || validity`
(DDS берёт один route_no с последним validity через rn=1).
- `ticket_price` → `price`, `boarding_seq IS NOT NULL` → `is_boarded = TRUE`
(реальные колонки dds.fact_flight_sales).
- Блок 2: добавлено пояснение, что STG-DAG всегда генерирует новый день,
поэтому тест идемпотентности — только ODS/DDS/DM без повторного запуска STG.
- Блок 5.2: уточнено, что ODS намеренно использует batch_id из STG вместо run_id
для сквозного lineage — это паттерн, а не баг.
- Проверка:
- все SQL-запросы из плана выполнены на стенде без ошибок.
This commit is contained in:
+33
-13
@@ -136,22 +136,23 @@ UNION ALL SELECT 'boarding_passes',
|
||||
|
||||
```sql
|
||||
-- Снапшот-таблицы: ODS = последний батч STG
|
||||
-- Примечание: поле называется batch_id (без подчёркивания), а не _batch_id
|
||||
SELECT
|
||||
'airports' AS entity,
|
||||
(SELECT COUNT(*) FROM stg.airports
|
||||
WHERE _batch_id = (SELECT MAX(_batch_id) FROM stg.airports)) AS stg_cnt,
|
||||
WHERE batch_id = (SELECT MAX(batch_id) FROM stg.airports)) AS stg_cnt,
|
||||
(SELECT COUNT(*) FROM ods.airports) AS ods_cnt
|
||||
UNION ALL SELECT 'airplanes',
|
||||
(SELECT COUNT(*) FROM stg.airplanes
|
||||
WHERE _batch_id = (SELECT MAX(_batch_id) FROM stg.airplanes)),
|
||||
WHERE batch_id = (SELECT MAX(batch_id) FROM stg.airplanes)),
|
||||
(SELECT COUNT(*) FROM ods.airplanes)
|
||||
UNION ALL SELECT 'routes',
|
||||
(SELECT COUNT(*) FROM stg.routes
|
||||
WHERE _batch_id = (SELECT MAX(_batch_id) FROM stg.routes)),
|
||||
WHERE batch_id = (SELECT MAX(batch_id) FROM stg.routes)),
|
||||
(SELECT COUNT(*) FROM ods.routes)
|
||||
UNION ALL SELECT 'seats',
|
||||
(SELECT COUNT(*) FROM stg.seats
|
||||
WHERE _batch_id = (SELECT MAX(_batch_id) FROM stg.seats)),
|
||||
WHERE batch_id = (SELECT MAX(batch_id) FROM stg.seats)),
|
||||
(SELECT COUNT(*) FROM ods.seats);
|
||||
```
|
||||
|
||||
@@ -180,8 +181,11 @@ UNION ALL SELECT 'passengers',
|
||||
|
||||
```sql
|
||||
-- Маршруты SCD2: текущих записей = уникальных бизнес-ключей в ODS
|
||||
-- Примечание: бизнес-ключ = route_no (не route_no || '-' || validity).
|
||||
-- Один route_no может иметь несколько периодов validity в ods.routes,
|
||||
-- но DDS берёт только самый свежий (rn=1 по validity DESC).
|
||||
SELECT
|
||||
(SELECT COUNT(DISTINCT route_no || '-' || validity) FROM ods.routes) AS ods_routes,
|
||||
(SELECT COUNT(DISTINCT route_no) FROM ods.routes) AS ods_routes,
|
||||
(SELECT COUNT(*) FROM dds.dim_routes WHERE valid_to IS NULL) AS dds_current,
|
||||
(SELECT COUNT(*) FROM dds.dim_routes) AS dds_total;
|
||||
```
|
||||
@@ -201,8 +205,9 @@ SELECT
|
||||
|
||||
```sql
|
||||
-- Общая выручка: fact vs sales_report
|
||||
-- Примечание: колонка называется price (не ticket_price)
|
||||
SELECT
|
||||
(SELECT SUM(ticket_price) FROM dds.fact_flight_sales) AS fact_revenue,
|
||||
(SELECT SUM(price) FROM dds.fact_flight_sales) AS fact_revenue,
|
||||
(SELECT SUM(total_revenue) FROM dm.sales_report) AS dm_revenue;
|
||||
```
|
||||
|
||||
@@ -219,13 +224,12 @@ SELECT
|
||||
**Ожидание:** совпадение (или объяснимая разница).
|
||||
|
||||
```sql
|
||||
-- Общее число посадок: fact vs airport_traffic
|
||||
-- Примечание: airport_traffic считает и вылеты и прилёты отдельно,
|
||||
-- поэтому dm_total_pax может быть ~2x от fact_boarded
|
||||
-- Общее число посадок: fact vs sales_report
|
||||
-- Примечание: колонка называется is_boarded (не boarding_seq)
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM dds.fact_flight_sales
|
||||
WHERE boarding_seq IS NOT NULL) AS fact_boarded,
|
||||
(SELECT SUM(total_passengers) FROM dm.airport_traffic) AS dm_total_pax;
|
||||
WHERE is_boarded = TRUE) AS fact_boarded,
|
||||
(SELECT SUM(passengers_boarded) FROM dm.sales_report) AS dm_boarded;
|
||||
```
|
||||
|
||||
#### E. Целостность суррогатных ключей (FK в DDS и DM)
|
||||
@@ -267,7 +271,19 @@ WHERE f.calendar_sk IS NOT NULL AND c.calendar_sk IS NULL;
|
||||
|
||||
## Блок 2: Идемпотентность (повторный Day 1)
|
||||
|
||||
**Цель:** повторный прогон тех же DAG'ов НЕ дублирует данные.
|
||||
**Цель:** повторный прогон ODS/DDS/DM НЕ дублирует данные при неизменённом STG.
|
||||
|
||||
**Важно:** `bookings_to_gp_stage` ВСЕГДА генерирует следующий день при запуске
|
||||
(через `generate_bookings_day`). Тест идемпотентности нужно проводить только для
|
||||
`bookings_to_gp_ods`, `bookings_to_gp_dds`, `bookings_to_gp_dm` — без повторного
|
||||
запуска STG. Убедитесь, что все STG-батчи уже обработаны ODS перед тестом:
|
||||
|
||||
```sql
|
||||
SELECT COUNT(*) AS unprocessed
|
||||
FROM stg.bookings
|
||||
WHERE load_dttm > (SELECT COALESCE(MAX(_load_ts), '1900-01-01') FROM ods.bookings);
|
||||
-- Ожидание: 0
|
||||
```
|
||||
|
||||
### 2.1 Зафиксировать counts после Day 1
|
||||
|
||||
@@ -484,8 +500,12 @@ grep -r '_batch_id' sql/dds/*_load.sql sql/dm/*_load.sql # ожидание: п
|
||||
|
||||
### 5.2 Все load.sql используют шаблон {{ run_id }}
|
||||
|
||||
Примечание: ODS намеренно не использует `{{ run_id }}` — вместо этого в `_load_id`
|
||||
сохраняется `batch_id` из STG для сквозного lineage (traceable to source batch).
|
||||
Это правильный паттерн, а не баг. Проверять нужно только STG/DDS/DM.
|
||||
|
||||
```bash
|
||||
for f in sql/stg/*_load.sql sql/ods/*_load.sql sql/dds/*_load.sql sql/dm/*_load.sql; do
|
||||
for f in sql/stg/*_load.sql sql/dds/*_load.sql sql/dm/*_load.sql; do
|
||||
if ! grep -q '{{ run_id }}\|{{ ti.xcom_pull' "$f"; then
|
||||
echo "WARN: $f не содержит {{ run_id }}"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user