Merge branch 'docs/zoom-ladder': лесенка схем, метод чтения пайплайна и таксономия DQ
This commit is contained in:
commit
98030f8b0d
7 files changed
+368
-143
No files matched your search
@@ -151,6 +151,8 @@ make clean # полный reset: удалить контейнер
|
||||
## Документация
|
||||
|
||||
- [Учебные задания](docs/assignment/README.md)
|
||||
- [Схема DWH: четыре уровня приближения](docs/design/db_schema.md)
|
||||
- [Как читать пайплайн по скрипту загрузки](docs/design/reading_the_pipeline.md)
|
||||
- [План тестирования/проверок и негативные кейсы](TESTING.md)
|
||||
- [Дополнительные заметки и технические детали](docs/README.md)
|
||||
- [Детали по STG DAG](docs/bookings_to_gp_stage.md)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
- [Быстрый старт и команды](../README.md)
|
||||
- [Учебные задания](assignment/README.md)
|
||||
- [Как читать пайплайн по скрипту загрузки](design/reading_the_pipeline.md)
|
||||
- [План тестирования и проверки](../TESTING.md)
|
||||
- [Главный учебный DAG: bookings → stg](bookings_to_gp_stage.md)
|
||||
- [Учебный DAG: stg -> ods](bookings_to_gp_ods.md)
|
||||
@@ -16,6 +17,7 @@
|
||||
|
||||
- [Единые конвенции нейминга DWH (служебные поля и SCD)](design/naming_conventions.md)
|
||||
- [Схема БД всех слоёв DWH](design/db_schema.md)
|
||||
- [Как читать пайплайн по скрипту загрузки](design/reading_the_pipeline.md)
|
||||
- [Дизайн-документ STG](design/bookings_stg_design.md)
|
||||
- [Дизайн-документ ODS](design/bookings_ods_design.md)
|
||||
- [Дизайн-документ DDS](design/bookings_dds_design.md)
|
||||
@@ -28,4 +30,5 @@
|
||||
- [Как устроен Docker-стенд (образы, Connections, переменные окружения)](stack.md)
|
||||
- [Известные проблемы bookings-db](reference/bookings_db_issues.md)
|
||||
- [QA-план отладки пайплайна](reference/qa-plan.md)
|
||||
- [Классы DQ-проверок](reference/dq_taxonomy.md)
|
||||
- [Порядок запуска DAG-ов](dag_execution_order.md)
|
||||
@@ -9,6 +9,12 @@
|
||||
На месте ваших заданий сейчас стоят **заглушки** (`SELECT 1;`). Вы замените
|
||||
их на реальную SQL-логику, и после этого весь DWH будет заполнен.
|
||||
|
||||
## С чего начать чтение
|
||||
|
||||
Сначала посмотрите [схему DWH по уровням приближения](../design/db_schema.md).
|
||||
Пока пишете свой слой, держите рядом [метод чтения скрипта загрузки](../design/reading_the_pipeline.md).
|
||||
Для `*_dq.sql` сверяйтесь с [классами DQ-проверок](../reference/dq_taxonomy.md).
|
||||
|
||||
## Перед началом
|
||||
|
||||
Убедитесь, что стенд запущен и данные загружены —
|
||||
|
||||
+228
-143
@@ -7,157 +7,88 @@
|
||||
> — заглушки (`SELECT 1;`). Данные появятся после реализации заданий.
|
||||
|
||||
Архитектура хранилища данных (DWH) для учебного проекта Airflow + Greenplum.
|
||||
Источник — демо-БД `bookings` (Postgres). Документ даёт цельный взгляд «сверху»;
|
||||
детали реализации — в дизайн-документах слоёв.
|
||||
Источник — демо-БД `bookings` (Postgres). Одна диаграмма отвечает на один вопрос:
|
||||
карта, которая показывает всё сразу, не даёт ясного ответа ни на один из них.
|
||||
|
||||
Понять, как устроен проект → лесенка ниже.
|
||||
|
||||
Пишете свой load-скрипт и хотите свериться → [`reading_the_pipeline.md`](reading_the_pipeline.md).
|
||||
|
||||
Какие DQ-проверки писать → [`dq_taxonomy.md`](../reference/dq_taxonomy.md).
|
||||
|
||||
Точечный вопрос «что кормит X?» → полная карта в конце.
|
||||
|
||||
---
|
||||
|
||||
## Полная схема потоков данных (Data Lineage)
|
||||
## Как устроен поток целиком?
|
||||
|
||||
Здесь показано движение данных через пять слоёв DWH.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
%% Стили
|
||||
classDef source fill:#e1f5fe,stroke:#01579b,stroke-width:2px;
|
||||
classDef stg fill:#fff9c4,stroke:#fbc02d,stroke-width:2px;
|
||||
classDef ods fill:#e0f2f1,stroke:#00695c,stroke-width:2px;
|
||||
classDef dim fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px;
|
||||
classDef fact fill:#ffccbc,stroke:#bf360c,stroke-width:4px;
|
||||
classDef dm fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px;
|
||||
|
||||
%% 1. Source
|
||||
subgraph Source_Postgres [Source: Postgres Bookings]
|
||||
direction TB
|
||||
SRC_Airports[airports_data]:::source
|
||||
SRC_Airplanes[airplanes_data]:::source
|
||||
SRC_Routes[routes]:::source
|
||||
SRC_Seats[seats]:::source
|
||||
SRC_Bookings[bookings]:::source
|
||||
SRC_Tickets[tickets]:::source
|
||||
SRC_Flights[flights]:::source
|
||||
SRC_Segments[segments]:::source
|
||||
SRC_Boarding[boarding_passes]:::source
|
||||
end
|
||||
|
||||
%% 2. STAGING (Load 1-to-1, AO-Row)
|
||||
subgraph STG_Layer [Layer: STG Staging]
|
||||
direction TB
|
||||
STG_Airports[stg.airports]:::stg
|
||||
STG_Airplanes[stg.airplanes]:::stg
|
||||
STG_Routes[stg.routes]:::stg
|
||||
STG_Seats[stg.seats]:::stg
|
||||
STG_Bookings[stg.bookings]:::stg
|
||||
STG_Tickets[stg.tickets]:::stg
|
||||
STG_Flights[stg.flights]:::stg
|
||||
STG_Segments[stg.segments]:::stg
|
||||
STG_Boarding[stg.boarding_passes]:::stg
|
||||
end
|
||||
|
||||
%% Links Source to STG
|
||||
SRC_Airports --> STG_Airports
|
||||
SRC_Airplanes --> STG_Airplanes
|
||||
SRC_Routes --> STG_Routes
|
||||
SRC_Seats --> STG_Seats
|
||||
SRC_Bookings --> STG_Bookings
|
||||
SRC_Tickets --> STG_Tickets
|
||||
SRC_Flights --> STG_Flights
|
||||
SRC_Segments --> STG_Segments
|
||||
SRC_Boarding --> STG_Boarding
|
||||
|
||||
%% 3. ODS (3NF, Clean, Type)
|
||||
subgraph ODS_Layer [Layer: ODS Operational Core]
|
||||
direction TB
|
||||
ODS_Airports[ods.airports]:::ods
|
||||
ODS_Airplanes[ods.airplanes]:::ods
|
||||
ODS_Routes[ods.routes]:::ods
|
||||
ODS_Seats[ods.seats]:::ods
|
||||
ODS_Bookings[ods.bookings]:::ods
|
||||
ODS_Tickets[ods.tickets]:::ods
|
||||
ODS_Flights[ods.flights]:::ods
|
||||
ODS_Segments[ods.segments]:::ods
|
||||
ODS_Boarding[ods.boarding_passes]:::ods
|
||||
end
|
||||
|
||||
%% Links STG to ODS
|
||||
STG_Airports --> ODS_Airports
|
||||
STG_Airplanes --> ODS_Airplanes
|
||||
STG_Routes --> ODS_Routes
|
||||
STG_Seats --> ODS_Seats
|
||||
STG_Bookings --> ODS_Bookings
|
||||
STG_Tickets --> ODS_Tickets
|
||||
STG_Flights --> ODS_Flights
|
||||
STG_Segments --> ODS_Segments
|
||||
STG_Boarding --> ODS_Boarding
|
||||
|
||||
%% 4. DDS (Star Schema)
|
||||
subgraph DDS_Layer [Layer: DDS Star Schema]
|
||||
direction TB
|
||||
DIM_Calendar[dds.dim_calendar]:::dim
|
||||
DIM_Airports[dds.dim_airports]:::dim
|
||||
DIM_Airplanes[dds.dim_airplanes]:::dim
|
||||
DIM_Tariffs[dds.dim_tariffs]:::dim
|
||||
DIM_Passengers[dds.dim_passengers]:::dim
|
||||
DIM_Routes[dds.dim_routes SCD2]:::dim
|
||||
FACT_Sales[dds.fact_flight_sales]:::fact
|
||||
end
|
||||
|
||||
%% ODS to DDS Dimensions
|
||||
ODS_Airports --> DIM_Airports
|
||||
ODS_Airplanes --> DIM_Airplanes
|
||||
ODS_Seats -.->|total_seats| DIM_Airplanes
|
||||
ODS_Segments -.->|DISTINCT| DIM_Tariffs
|
||||
ODS_Tickets -->|Unique passengers| DIM_Passengers
|
||||
ODS_Routes -->|SCD2 hashdiff| DIM_Routes
|
||||
DIM_Airports -.->|cities| DIM_Routes
|
||||
DIM_Airplanes -.->|model, seats| DIM_Routes
|
||||
|
||||
%% ODS to Fact
|
||||
ODS_Segments -->|Main stream| FACT_Sales
|
||||
ODS_Tickets -->|book_ref, passenger_id| FACT_Sales
|
||||
ODS_Bookings -->|book_date| FACT_Sales
|
||||
ODS_Flights -->|schedule, route_no| FACT_Sales
|
||||
ODS_Routes -->|dep/arr airports| FACT_Sales
|
||||
ODS_Boarding -->|LEFT JOIN seat_no| FACT_Sales
|
||||
|
||||
%% Dimensions to Fact
|
||||
DIM_Calendar -->|calendar_sk| FACT_Sales
|
||||
DIM_Airports -->|dep/arr _sk| FACT_Sales
|
||||
DIM_Airplanes -->|airplane_sk| FACT_Sales
|
||||
DIM_Tariffs -->|tariff_sk| FACT_Sales
|
||||
DIM_Passengers -->|passenger_sk| FACT_Sales
|
||||
DIM_Routes -->|route_sk| FACT_Sales
|
||||
|
||||
%% 5. DM (Vitrines)
|
||||
subgraph DM_Layer [Layer: DM Data Marts]
|
||||
direction TB
|
||||
DM_Sales[dm.sales_report]:::dm
|
||||
DM_Traffic[dm.airport_traffic]:::dm
|
||||
DM_Route[dm.route_performance]:::dm
|
||||
DM_Monthly[dm.monthly_overview]:::dm
|
||||
DM_Loyalty[dm.passenger_loyalty]:::dm
|
||||
end
|
||||
|
||||
%% DDS to DM
|
||||
FACT_Sales --> DM_Sales
|
||||
FACT_Sales --> DM_Traffic
|
||||
FACT_Sales --> DM_Route
|
||||
FACT_Sales --> DM_Monthly
|
||||
FACT_Sales --> DM_Loyalty
|
||||
DIM_Airports -.-> DM_Sales
|
||||
DIM_Airports -.-> DM_Traffic
|
||||
DIM_Tariffs -.-> DM_Sales
|
||||
DIM_Tariffs -.-> DM_Loyalty
|
||||
DIM_Calendar -.-> DM_Sales
|
||||
DIM_Calendar -.-> DM_Traffic
|
||||
DIM_Calendar -.-> DM_Route
|
||||
DIM_Calendar -.-> DM_Monthly
|
||||
DIM_Calendar -.-> DM_Loyalty
|
||||
DIM_Routes -.-> DM_Route
|
||||
DIM_Routes -.-> DM_Monthly
|
||||
DIM_Routes -.-> DM_Loyalty
|
||||
DIM_Airplanes -.-> DM_Monthly
|
||||
DIM_Passengers -.-> DM_Loyalty
|
||||
SRC["Source<br/>Postgres bookings"] --> STG["STG<br/>сырые данные"]
|
||||
STG --> ODS["ODS<br/>очищенные данные и типы"]
|
||||
ODS --> DDS["DDS<br/>факты и измерения"]
|
||||
DDS --> DM["DM<br/>витрины для бизнес-вопросов"]
|
||||
```
|
||||
|
||||
## Как выглядит звезда?
|
||||
|
||||
Здесь показан факт и шесть его измерений.
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
CAL["dds.dim_calendar"] --> FACT["dds.fact_flight_sales"]
|
||||
AIRPORT["dds.dim_airports"] --> FACT
|
||||
AIRPLANE["dds.dim_airplanes"] --> FACT
|
||||
TARIFF["dds.dim_tariffs"] --> FACT
|
||||
PASSENGER["dds.dim_passengers"] --> FACT
|
||||
ROUTE["dds.dim_routes<br/>SCD2"] --> FACT
|
||||
```
|
||||
|
||||
## Как собирается факт?
|
||||
|
||||
Здесь показаны таблицы, которые `fact_src` читает при сборке факта.
|
||||
Сплошные стрелки обозначают `INNER JOIN`. Пунктирные стрелки обозначают `LEFT JOIN`.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
SEG["ods.segments"] -->|"Main stream: grain, price"| FACT["dds.fact_flight_sales"]
|
||||
TKT["ods.tickets"] -->|"book_ref, passenger_id"| FACT
|
||||
BKG["ods.bookings"] -->|"book_date"| FACT
|
||||
FLT["ods.flights"] -->|"schedule, route_no"| FACT
|
||||
ODS_RTE["ods.routes"] -.->|"dep/arr airports"| FACT
|
||||
CAL["dds.dim_calendar"] -.->|"calendar_sk"| FACT
|
||||
AIRPORT["dds.dim_airports"] -.->|"dep/arr _sk"| FACT
|
||||
AIRPLANE["dds.dim_airplanes"] -.->|"airplane_sk"| FACT
|
||||
TARIFF["dds.dim_tariffs"] -.->|"tariff_sk"| FACT
|
||||
PASSENGER["dds.dim_passengers"] -.->|"passenger_sk"| FACT
|
||||
ROUTE["dds.dim_routes"] -.->|"route_sk"| FACT
|
||||
BOARDING["ods.boarding_passes"] -.->|"LEFT JOIN seat_no"| FACT
|
||||
```
|
||||
|
||||
`ods.routes` оставлен отдельным узлом: через него факт находит аэропорты
|
||||
вылета и прилёта, не завися от студенческого `dim_routes`. Схема показывает
|
||||
замысел загрузки. На ветке `main` поля `route_sk`, `airplane_sk` и
|
||||
`passenger_sk` могут быть `NULL`, пока студент не реализовал измерения.
|
||||
Почему это не всегда ошибка, объяснено в [справочнике DQ](../reference/dq_taxonomy.md#null--не-всегда-ошибка).
|
||||
|
||||
## Что кормит витрины?
|
||||
|
||||
Здесь показано, во что расходятся строки факта.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
FACT["dds.fact_flight_sales"] -->|"день × аэропорты × тариф"| SALES["dm.sales_report<br/>выручка и посадка"]
|
||||
FACT -->|"день × аэропорт"| TRAFFIC["dm.airport_traffic<br/>пассажиропоток"]
|
||||
FACT -->|"route_bk"| ROUTE["dm.route_performance<br/>эффективность маршрута"]
|
||||
FACT -->|"месяц × airplane_sk"| MONTHLY["dm.monthly_overview<br/>динамика по самолётам"]
|
||||
FACT -->|"passenger_sk"| LOYALTY["dm.passenger_loyalty<br/>профиль лояльности"]
|
||||
```
|
||||
|
||||
Измерения уточняют даты, аэропорты, тарифы, маршруты, самолёты и пассажиров,
|
||||
но основной поток во все пять витрин начинается с `fact_flight_sales`.
|
||||
|
||||
---
|
||||
|
||||
## Сводка объектов по слоям
|
||||
@@ -310,5 +241,159 @@ Degenerate keys: `book_ref`, `ticket_no`, `flight_id`, `book_date`, `seat_no`.
|
||||
- [`bookings_dds_design.md`](bookings_dds_design.md) — дизайн DDS (Star Schema, SCD2)
|
||||
- [`bookings_dm_design.md`](bookings_dm_design.md) — дизайн DM (5 витрин)
|
||||
- [`naming_conventions.md`](naming_conventions.md) — нейминг полей
|
||||
- [`reading_the_pipeline.md`](reading_the_pipeline.md) — как читать связи из скрипта загрузки
|
||||
- [`../reference/dq_taxonomy.md`](../reference/dq_taxonomy.md) — классы DQ-проверок и допустимые NULL
|
||||
- Часовые пояса и настройка PXF — в ветке `solution` (`docs/reference/`)
|
||||
- [`../assignment/analyst_spec.md`](../assignment/analyst_spec.md) — ТЗ от аналитика (курсовое задание)
|
||||
|
||||
---
|
||||
|
||||
## Полная карта lineage (справочная)
|
||||
|
||||
Здесь собран цельный взгляд «сверху». Эта карта нужна для точечного поиска:
|
||||
например, «что кормит X?» или «куда идут данные из X?». Читать весь пайплайн
|
||||
по ней подряд не нужно.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
%% Стили
|
||||
classDef source fill:#e1f5fe,stroke:#01579b,stroke-width:2px;
|
||||
classDef stg fill:#fff9c4,stroke:#fbc02d,stroke-width:2px;
|
||||
classDef ods fill:#e0f2f1,stroke:#00695c,stroke-width:2px;
|
||||
classDef dim fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px;
|
||||
classDef fact fill:#ffccbc,stroke:#bf360c,stroke-width:4px;
|
||||
classDef dm fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px;
|
||||
|
||||
%% 1. Source
|
||||
subgraph Source_Postgres [Source: Postgres Bookings]
|
||||
direction TB
|
||||
SRC_Airports[airports_data]:::source
|
||||
SRC_Airplanes[airplanes_data]:::source
|
||||
SRC_Routes[routes]:::source
|
||||
SRC_Seats[seats]:::source
|
||||
SRC_Bookings[bookings]:::source
|
||||
SRC_Tickets[tickets]:::source
|
||||
SRC_Flights[flights]:::source
|
||||
SRC_Segments[segments]:::source
|
||||
SRC_Boarding[boarding_passes]:::source
|
||||
end
|
||||
|
||||
%% 2. STAGING (Load 1-to-1, AO-Row)
|
||||
subgraph STG_Layer [Layer: STG Staging]
|
||||
direction TB
|
||||
STG_Airports[stg.airports]:::stg
|
||||
STG_Airplanes[stg.airplanes]:::stg
|
||||
STG_Routes[stg.routes]:::stg
|
||||
STG_Seats[stg.seats]:::stg
|
||||
STG_Bookings[stg.bookings]:::stg
|
||||
STG_Tickets[stg.tickets]:::stg
|
||||
STG_Flights[stg.flights]:::stg
|
||||
STG_Segments[stg.segments]:::stg
|
||||
STG_Boarding[stg.boarding_passes]:::stg
|
||||
end
|
||||
|
||||
%% Links Source to STG
|
||||
SRC_Airports --> STG_Airports
|
||||
SRC_Airplanes --> STG_Airplanes
|
||||
SRC_Routes --> STG_Routes
|
||||
SRC_Seats --> STG_Seats
|
||||
SRC_Bookings --> STG_Bookings
|
||||
SRC_Tickets --> STG_Tickets
|
||||
SRC_Flights --> STG_Flights
|
||||
SRC_Segments --> STG_Segments
|
||||
SRC_Boarding --> STG_Boarding
|
||||
|
||||
%% 3. ODS (3NF, Clean, Type)
|
||||
subgraph ODS_Layer [Layer: ODS Operational Core]
|
||||
direction TB
|
||||
ODS_Airports[ods.airports]:::ods
|
||||
ODS_Airplanes[ods.airplanes]:::ods
|
||||
ODS_Routes[ods.routes]:::ods
|
||||
ODS_Seats[ods.seats]:::ods
|
||||
ODS_Bookings[ods.bookings]:::ods
|
||||
ODS_Tickets[ods.tickets]:::ods
|
||||
ODS_Flights[ods.flights]:::ods
|
||||
ODS_Segments[ods.segments]:::ods
|
||||
ODS_Boarding[ods.boarding_passes]:::ods
|
||||
end
|
||||
|
||||
%% Links STG to ODS
|
||||
STG_Airports --> ODS_Airports
|
||||
STG_Airplanes --> ODS_Airplanes
|
||||
STG_Routes --> ODS_Routes
|
||||
STG_Seats --> ODS_Seats
|
||||
STG_Bookings --> ODS_Bookings
|
||||
STG_Tickets --> ODS_Tickets
|
||||
STG_Flights --> ODS_Flights
|
||||
STG_Segments --> ODS_Segments
|
||||
STG_Boarding --> ODS_Boarding
|
||||
|
||||
%% 4. DDS (Star Schema)
|
||||
subgraph DDS_Layer [Layer: DDS Star Schema]
|
||||
direction TB
|
||||
DIM_Calendar[dds.dim_calendar]:::dim
|
||||
DIM_Airports[dds.dim_airports]:::dim
|
||||
DIM_Airplanes[dds.dim_airplanes]:::dim
|
||||
DIM_Tariffs[dds.dim_tariffs]:::dim
|
||||
DIM_Passengers[dds.dim_passengers]:::dim
|
||||
DIM_Routes[dds.dim_routes SCD2]:::dim
|
||||
FACT_Sales[dds.fact_flight_sales]:::fact
|
||||
end
|
||||
|
||||
%% ODS to DDS Dimensions
|
||||
ODS_Airports --> DIM_Airports
|
||||
ODS_Airplanes --> DIM_Airplanes
|
||||
ODS_Seats -.->|total_seats| DIM_Airplanes
|
||||
ODS_Segments -.->|DISTINCT| DIM_Tariffs
|
||||
ODS_Tickets -->|Unique passengers| DIM_Passengers
|
||||
ODS_Routes -->|SCD2 hashdiff| DIM_Routes
|
||||
DIM_Airports -.->|cities| DIM_Routes
|
||||
DIM_Airplanes -.->|model, seats| DIM_Routes
|
||||
|
||||
%% ODS to Fact
|
||||
ODS_Segments -->|Main stream| FACT_Sales
|
||||
ODS_Tickets -->|book_ref, passenger_id| FACT_Sales
|
||||
ODS_Bookings -->|book_date| FACT_Sales
|
||||
ODS_Flights -->|schedule, route_no| FACT_Sales
|
||||
ODS_Routes -->|dep/arr airports| FACT_Sales
|
||||
ODS_Boarding -->|LEFT JOIN seat_no| FACT_Sales
|
||||
|
||||
%% Dimensions to Fact
|
||||
DIM_Calendar -->|calendar_sk| FACT_Sales
|
||||
DIM_Airports -->|dep/arr _sk| FACT_Sales
|
||||
DIM_Airplanes -->|airplane_sk| FACT_Sales
|
||||
DIM_Tariffs -->|tariff_sk| FACT_Sales
|
||||
DIM_Passengers -->|passenger_sk| FACT_Sales
|
||||
DIM_Routes -->|route_sk| FACT_Sales
|
||||
|
||||
%% 5. DM (Vitrines)
|
||||
subgraph DM_Layer [Layer: DM Data Marts]
|
||||
direction TB
|
||||
DM_Sales[dm.sales_report]:::dm
|
||||
DM_Traffic[dm.airport_traffic]:::dm
|
||||
DM_Route[dm.route_performance]:::dm
|
||||
DM_Monthly[dm.monthly_overview]:::dm
|
||||
DM_Loyalty[dm.passenger_loyalty]:::dm
|
||||
end
|
||||
|
||||
%% DDS to DM
|
||||
FACT_Sales --> DM_Sales
|
||||
FACT_Sales --> DM_Traffic
|
||||
FACT_Sales --> DM_Route
|
||||
FACT_Sales --> DM_Monthly
|
||||
FACT_Sales --> DM_Loyalty
|
||||
DIM_Airports -.-> DM_Sales
|
||||
DIM_Airports -.-> DM_Traffic
|
||||
DIM_Tariffs -.-> DM_Sales
|
||||
DIM_Tariffs -.-> DM_Loyalty
|
||||
DIM_Calendar -.-> DM_Sales
|
||||
DIM_Calendar -.-> DM_Traffic
|
||||
DIM_Calendar -.-> DM_Route
|
||||
DIM_Calendar -.-> DM_Monthly
|
||||
DIM_Calendar -.-> DM_Loyalty
|
||||
DIM_Routes -.-> DM_Route
|
||||
DIM_Routes -.-> DM_Monthly
|
||||
DIM_Routes -.-> DM_Loyalty
|
||||
DIM_Airplanes -.-> DM_Monthly
|
||||
DIM_Passengers -.-> DM_Loyalty
|
||||
```
|
||||
@@ -0,0 +1,65 @@
|
||||
# Как читать пайплайн по скрипту загрузки
|
||||
|
||||
Большую схему не нужно запоминать. Связи любой таблицы уже записаны в блоке
|
||||
`FROM ... JOIN ...` её файла `*_load.sql`. Обычно каждая прочитанная таблица
|
||||
даёт одно входящее ребро. Если одна таблица читается под разными псевдонимами,
|
||||
как `dim_airports`, её можно показать одним узлом.
|
||||
|
||||
## Метод
|
||||
|
||||
`INNER JOIN` показывает обязательную часть основного потока. Если входной строки
|
||||
нет, целевая строка не появится. `LEFT JOIN` к измерениям решает две задачи.
|
||||
Факт может прийти раньше измерения: это опаздывающее измерение
|
||||
(late-arriving dimension). Эталонный факт также не должен зависеть от того,
|
||||
успел ли студент реализовать свои измерения. Поэтому строка факта сохраняется,
|
||||
а её суррогатный ключ пока может быть `NULL`.
|
||||
|
||||
В этом CTE есть и другие `LEFT JOIN`. `ods.routes` даёт эталонный путь к
|
||||
аэропортам, а `ods.boarding_passes` содержит необязательные `seat_no` и
|
||||
признак посадки.
|
||||
|
||||
Если в загрузчике несколько SQL-операторов, смотрите на источник `INSERT`.
|
||||
Именно он задаёт полный список входящих связей для новых строк. Например, в
|
||||
`fact_flight_sales_load.sql` блок `FROM` у `UPDATE` читает только изменяемые
|
||||
поля, а полный список находится в CTE `fact_src` перед `INSERT`.
|
||||
|
||||
## Пример: `fact_flight_sales`
|
||||
|
||||
Откройте [`sql/dds/fact_flight_sales_load.sql`](../../sql/dds/fact_flight_sales_load.sql)
|
||||
и пройдите `fact_src` сверху вниз:
|
||||
|
||||
1. `FROM ods.segments` задаёт зерно `(ticket_no, flight_id)` и поле `price`.
|
||||
2. `JOIN ods.tickets` обязателен для `book_ref` и `passenger_id`.
|
||||
3. `JOIN ods.bookings` обязателен для `book_date`.
|
||||
4. `JOIN ods.flights` обязателен для расписания и `route_no`.
|
||||
5. `LEFT JOIN ods.routes` находит аэропорты по `route_no`. Этот эталонный путь
|
||||
не зависит от студенческого `dim_routes`.
|
||||
6. `LEFT JOIN dds.dim_routes` ищет point-in-time версию маршрута и `route_sk`.
|
||||
7. `LEFT JOIN dds.dim_calendar` даёт `calendar_sk` по дате вылета.
|
||||
8. Два `LEFT JOIN dds.dim_airports` дают ключи аэропортов вылета и прилёта.
|
||||
На схеме их можно показать одним узлом `dim_airports`.
|
||||
9. `LEFT JOIN dds.dim_airplanes` даёт `airplane_sk` через найденную версию маршрута.
|
||||
10. `LEFT JOIN dds.dim_tariffs` даёт `tariff_sk`.
|
||||
11. `LEFT JOIN dds.dim_passengers` даёт `passenger_sk`.
|
||||
12. `LEFT JOIN ods.boarding_passes` даёт `seat_no` и признак `is_boarded`.
|
||||
|
||||
Так список связей превращается в [схему «Как собирается факт?»](db_schema.md#как-собирается-факт):
|
||||
четыре обязательных входа рисуются сплошными стрелками, а остальные пунктирными.
|
||||
|
||||
## Как проверить свой слой
|
||||
|
||||
- Выпишите все таблицы из `FROM` и `JOIN` своего `*_load.sql`.
|
||||
- Сравните список с эталонной схемой или дизайн-документом этого слоя.
|
||||
- Проверьте служебные и SCD-поля по [`naming_conventions.md`](naming_conventions.md).
|
||||
- Убедитесь, что рядом есть `*_dq.sql` и он покрывает нужные
|
||||
[классы DQ-проверок](../reference/dq_taxonomy.md).
|
||||
|
||||
## Шаблон схемы одной таблицы
|
||||
|
||||
Скопируйте шаблон и замените имена таблиц и полей:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
MAIN["schema.main_input"] -->|"grain, metric"| TARGET["schema.target"]
|
||||
LOOKUP["schema.lookup"] -.->|"lookup_key"| TARGET
|
||||
```
|
||||
@@ -0,0 +1,63 @@
|
||||
# Классы 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#ключевые-договорённости).
|
||||
@@ -14,6 +14,7 @@
|
||||
>
|
||||
> Зависимость: перед запуском этого плана нужно починить bookings-db
|
||||
> (см. `docs/reference/bookings_db_issues.md`).
|
||||
> Классы проверок для разбора ошибок: [`dq_taxonomy.md`](dq_taxonomy.md).
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in new issue
Block a user