From 10a36b6dfbf045b97394cf5e403fd9822d82bb27 Mon Sep 17 00:00:00 2001 From: Dmitry Dementev Date: Sat, 17 Jan 2026 22:27:05 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BF=D0=BB=D0=B0=D0=BD=20=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=80=D0=B8=D0=BD=20=D0=B4=D0=BE=20stg=20=D1=81=D0=BB=D0=BE?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/internal/db_schema.md | 222 +++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 docs/internal/db_schema.md diff --git a/docs/internal/db_schema.md b/docs/internal/db_schema.md new file mode 100644 index 0000000..30b6d7a --- /dev/null +++ b/docs/internal/db_schema.md @@ -0,0 +1,222 @@ +# Схема БД DWH (Bookings → Greenplum) + +> **Статус:** Проект в разработке. Реализован только STG слой (частично: bookings, tickets). + +## Обзор + +Эта документация описывает архитектуру хранилища данных (DWH) для учебного проекта Airflow + Greenplum. Источник данных — демо-БД `bookings` (Postgres). + +### Статус реализации по слоям + +| Слой | Статус | Реализовано | +|------|--------|-------------| +| **Source** | ✅ Готово | Демо-БД bookings (Postgres) | +| **STG** | ⚠️ В процессе | 2 из 8 таблиц (bookings, tickets) | +| **DQ** | ⚠️ В процессе | Есть скрипты для bookings и tickets | +| **ODS** | ❌ Не реализован | Планируется | +| **DDS** | ❌ Не реализован | Планируется | + +--- + +## Полная схема потоков данных (Data Lineage) + +```mermaid +graph LR + %% Стили + classDef source fill:#e1f5fe,stroke:#01579b,stroke-width:2px; + classDef stg fill:#fff9c4,stroke:#fbc02d,stroke-width:2px; + classDef dq fill:#ffe0b2,stroke:#ef6c00,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; + + %% 1. Source + subgraph Source_Postgres [Source: Postgres Bookings] + direction TB + SRC_Airports[airports_data]:::source + SRC_Airplanes[airplanes_data]:::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_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_Seats --> STG_Seats + SRC_Bookings --> STG_Bookings + SRC_Tickets --> STG_Tickets + SRC_Flights --> STG_Flights + SRC_Segments --> STG_Segments + SRC_Boarding --> STG_Boarding + + %% 3. DATA QUALITY (Quality Checks) + subgraph DQ_Layer [Layer: DQ Data Quality] + direction TB + DQ_Bookings[dq.bookings_checks]:::dq + DQ_Tickets[dq.tickets_checks]:::dq + DQ_Flights[dq.flights_checks]:::dq + DQ_Segments[dq.segments_checks]:::dq + end + + %% Links STG to DQ + STG_Bookings --> DQ_Bookings + STG_Tickets --> DQ_Tickets + STG_Flights --> DQ_Flights + STG_Segments --> DQ_Segments + + %% 4. ODS (3NF, Clean, Type, Heap) + subgraph ODS_Layer [Layer: ODS Operational Core] + direction TB + ODS_Airports[ods.airports]:::ods + ODS_Airplanes[ods.airplanes]:::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 DQ to ODS + DQ_Bookings --> ODS_Bookings + DQ_Tickets --> ODS_Tickets + DQ_Flights --> ODS_Flights + DQ_Segments --> ODS_Segments + STG_Airports --> ODS_Airports + STG_Airplanes --> ODS_Airplanes + STG_Seats --> ODS_Seats + STG_Boarding --> ODS_Boarding + + %% 5. DDS (Star Schema) + subgraph DDS_Layer [Layer: DDS Star Schema] + direction TB + + %% Dimensions + DIM_Calendar[dim.calendar]:::dim + DIM_Airports[dim.airports]:::dim + DIM_Airplanes[dim.airplanes]:::dim + DIM_Tariffs[dim.tariffs]:::dim + DIM_Passengers[dim.passengers]:::dim + + %% Fact + FACT_Sales[fact.flight_sales]:::fact + end + + %% Transformations ODS to DDS + + %% Form reference tables + ODS_Airports --> DIM_Airports + + ODS_Airplanes --> DIM_Airplanes + ODS_Seats -.->|Enrich total_seats| DIM_Airplanes + + ODS_Segments -.->|Extract distinct| DIM_Tariffs + + ODS_Tickets -->|Extract Unique| DIM_Passengers + + %% Fact assembly (Main process) + ODS_Segments -->|Main Stream| FACT_Sales + ODS_Tickets -->|Join book_ref passenger_id| FACT_Sales + ODS_Flights -->|Join Times Status| FACT_Sales + ODS_Boarding -->|LEFT JOIN Seat No| FACT_Sales + + %% Link dimensions to fact + DIM_Calendar -->|calendar_sk| FACT_Sales + DIM_Airports -->|departure_airport_sk| FACT_Sales + DIM_Airports -->|arrival_airport_sk| FACT_Sales + DIM_Airplanes -->|airplane_sk| FACT_Sales + DIM_Tariffs -->|tariff_sk| FACT_Sales + DIM_Passengers -->|passenger_sk| FACT_Sales +``` + +--- + +## Пояснения к схеме (для студентов) + +Эта диаграмма покрывает 100% таблиц источника и показывает логику их трансформации. Вот на что стоит обратить внимание при обучении: + +### 1. Ветка справочников (Reference Data) + +* **`seats` + `airplanes` → `dim.airplanes`**: Здесь мы показываем пример **обогащения**. Таблица `seats` сама по себе в аналитике редко нужна отдельной сущностью. Мы используем её в ODS, чтобы посчитать общее количество мест (`total_seats`) и добавить это как атрибут в измерение самолётов (`dim.airplanes`). + +* **`airports` → `dim.airports`**: Простой перенос (1-в-1), но в DDS мы можем добавить, например, поле `city_ru` и `city_en` как отдельные колонки, убрав JSON, который есть в источнике. + +### 2. Ветка генерации измерений (Dimension Generation) + +* **`tickets` → `dim.passengers`**: Это самая сложная трансформация для измерения. В источнике нет таблицы "Пассажиры". Мы должны объяснить студентам, что мы "майним" пассажиров из билетов. Важно: один и тот же пассажир может иметь разные записи с разными именами (опечатки, изменение фамилии), поэтому нужна логика SCD Type 2 для отслеживания изменений. + +* **`segments` → `dim.tariffs`**: Таблицы тарифов физически нет в источнике, она "зашита" строкой (Economy, Business) в таблице полётов. Мы выносим её в отдельный справочник (Нормализация), чтобы в факте хранить маленький `INT` ключ, а не длинную строку. + +### 3. Сборка Факта (`fact.flight_sales`) + +Это центр звезды. Мы собираем его из четырёх ODS таблиц: + +1. **`ods.segments`**: Основа (зерно факта — один полётный сегмент билета). Дает сумму (`amount`). +2. **`ods.tickets`**: Приджойниваем, чтобы получить `book_ref` и `passenger_id`. +3. **`ods.flights`**: Приджойниваем, чтобы получить точное время вылета/прилета (для FK на календарь) и статусы. +4. **`ods.boarding_passes`**: Приджойниваем (LEFT JOIN), чтобы узнать, **сел ли пассажир реально в самолёт** и на какое место (`seat_no`). Это важный бизнес-аспект: билет куплен, но посадочный не выдан = пассажир не летел. + +### 4. Почему нет `dim.bookings`? + +В классической Star Schema измерения — это справочники (airports, aircrafts, passengers), а факты — транзакции/события (sales, bookings). + +`bookings` — это транзакционная таблица, а не справочник. Вместо отдельного измерения `dim.bookings` мы храним: +- `book_ref` — бизнес-ключ бронирования (в факте) +- `book_date` — дата бронирования (в факте) + +Это позволяет отвечать на вопросы типа: *"За сколько дней до вылета люди обычно покупают билеты?"* (разница между `book_date` и датой вылета из `dim.calendar`). + +### 5. Суррогатные ключи (Surrogate Keys) + +В Star Schema факт должен ссылаться на суррогатные ключи (SK) измерений, а не на бизнес-ключи: + +| Бизнес-ключ | Суррогатный ключ | Преимущество | +|-------------|------------------|--------------| +| `airport_code CHAR(3)` | `airport_sk INT` | Меньший размер, стабильность | +| `airplane_code TEXT` | `airplane_sk INT` | Меньший размер, стабильность | +| `passenger_id TEXT` | `passenger_sk INT` | Меньший размер, отслеживание изменений | + +### 6. Слой DQ (Data Quality) + +Между STG и ODS добавлен слой Data Quality для проверки качества данных. В проекте уже есть скрипты: +- `sql/stg/bookings_dq.sql` +- `sql/stg/tickets_dq.sql` + +На схеме показаны примеры проверок для всех таблиц, которые должны быть реализованы. + +--- + +## История изменений + +| Дата | Версия | Описание изменений | +|------|--------|-------------------| +| 2025-01-17 | 1.1 | Исправлены названия таблиц (`aircrafts_data` → `airplanes_data`, `ticket_flights` → `segments`), удалено `dim.bookings`, добавлены суррогатные ключи, добавлен слой DQ, исправлены связи | +| 2025-01-XX | 1.0 | Первоначальная версия | + +--- + +## TODO + +- [ ] Реализовать STG слой полностью (все 8 таблиц) +- [ ] Реализовать DQ слой для всех таблиц +- [ ] Реализовать ODS слой +- [ ] Реализовать DDS слой (измерения и факт) +- [ ] Создать DAG для загрузки ODS +- [ ] Создать DAG для загрузки DDS