Files
airflow-greenplum/sql/dm/route_performance_load.sql
T
ddadminandClaude Sonnet 4.6 815a248841 chore(bookings): полировка эталонного среза — код, документация, ANALYZE
- Зачем:
  - подготовка эталонного пайплайна к курсовой: код должен быть чистым,
    документация — актуальной, все слои — консистентными.
- Что:
  - добавлен ANALYZE во все 5 DM load-скриптов (был в STG/ODS/DDS, пропущен в DM).
  - README актуализирован: добавлен DM-слой в быстрый старт, список DAG'ов,
    исправлен дефолт BOOKINGS_JOBS (2→1), добавлены примеры запросов DM.
  - удалены остатки CSV-пайплайна: CSV_DIR/CSV_ROWS из docs/stack.md.
  - docs/stack.md: исправлен дефолт BOOKINGS_JOBS (2→1).
  - убран отладочный комментарий «Исправлено:» в passenger_loyalty_load.sql.
  - исправлено форматирование (black) в 4 файлах.
  - TODO.md: отмечены пройденные пункты Этапа 1.5.
- Проверка:
  - make lint && make test — проходят.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 19:37:35 +03:00

84 lines
3.4 KiB
SQL

-- Загрузка витрины dm.route_performance: Full Rebuild.
--
-- Учебные цели:
-- 1. Демонстрация TRUNCATE + INSERT. Таблица маленькая (~1000 строк),
-- пересоздать её с нуля дешевле, чем вычислять дельту.
-- 2. Обработка SCD2: агрегируем факты по бизнес-ключу route_bk,
-- чтобы собрать данные со всех исторических версий маршрута.
-- 3. Денормализация атрибутов из ТЕКУЩЕЙ (актуальной) версии измерения.
-- Шаг 1: Очистка таблицы (AO Column Store не поддерживает эффективный DELETE/UPDATE).
TRUNCATE dm.route_performance;
-- Шаг 2: Агрегация всех фактов по бизнес-ключу маршрута.
-- Используем временную таблицу для удобства и читаемости.
CREATE TEMP TABLE tmp_route_metrics ON COMMIT DROP AS
SELECT
r.route_bk,
COUNT(DISTINCT f.flight_id) AS total_flights,
COUNT(*) AS total_tickets,
SUM(CASE WHEN f.is_boarded THEN 1 ELSE 0 END) AS total_boarded,
SUM(f.price) AS total_revenue,
MIN(cal.date_actual) AS first_flight_date,
MAX(cal.date_actual) AS last_flight_date,
-- Доля посаженных пассажиров среди всех проданных билетов маршрута
-- (упрощение: не взвешено по рейсам)
AVG(CASE WHEN f.is_boarded THEN 1 ELSE 0 END::NUMERIC) AS avg_boarding_rate
FROM dds.fact_flight_sales f
JOIN dds.dim_routes r ON f.route_sk = r.route_sk
JOIN dds.dim_calendar cal ON f.calendar_sk = cal.calendar_sk
GROUP BY r.route_bk;
-- Шаг 3: Объединяем агрегаты с АКТУАЛЬНЫМИ атрибутами маршрута.
-- Учебный комментарий: Благодаря денормализации dim_routes — один JOIN вместо четырёх.
INSERT INTO dm.route_performance (
route_bk,
route_sk,
departure_airport_bk,
departure_city,
arrival_airport_bk,
arrival_city,
airplane_bk,
airplane_model,
total_seats,
total_flights,
total_tickets,
total_boarded,
total_revenue,
avg_ticket_price,
avg_boarding_rate,
avg_load_factor,
first_flight_date,
last_flight_date,
_load_id
)
SELECT
m.route_bk,
r_curr.route_sk,
r_curr.departure_airport AS departure_airport_bk,
r_curr.departure_city,
r_curr.arrival_airport AS arrival_airport_bk,
r_curr.arrival_city,
r_curr.airplane_code AS airplane_bk,
r_curr.airplane_model,
r_curr.total_seats,
m.total_flights,
m.total_tickets,
m.total_boarded,
m.total_revenue,
-- Средний чек
(m.total_revenue / NULLIF(m.total_tickets, 0))::NUMERIC(10,2),
m.avg_boarding_rate::NUMERIC(5,4),
-- Load Factor: общее кол-во посадок / (кол-во рейсов * мест в самолете)
ROUND(
m.total_boarded::NUMERIC / NULLIF(m.total_flights * r_curr.total_seats, 0),
4
) AS avg_load_factor,
m.first_flight_date,
m.last_flight_date,
'{{ run_id }}' AS _load_id
FROM tmp_route_metrics m
JOIN dds.dim_routes r_curr ON m.route_bk = r_curr.route_bk AND r_curr.valid_to IS NULL;
ANALYZE dm.route_performance;