- Зачем:
- подготовка эталонного пайплайна к курсовой: код должен быть чистым,
документация — актуальной, все слои — консистентными.
- Что:
- добавлен 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>
168 lines
6.6 KiB
SQL
168 lines
6.6 KiB
SQL
-- Загрузка витрины dm.monthly_overview: инкрементальный UPSERT по месяцам.
|
|
--
|
|
-- Учебные цели:
|
|
-- 1. Двухуровневая агрегация: чтобы честно посчитать среднюю заполняемость (avg_load_factor),
|
|
-- мы сначала считаем её для КАЖДОГО рейса, а затем берем среднее по месяцу.
|
|
-- 2. Ограничения SCD1: dim_airplanes — это SCD1-измерение. Мы берем total_seats из
|
|
-- его текущего состояния. Если бы самолет переоборудовали (изменили число мест) в прошлом,
|
|
-- для точного исторического расчета нам потребовалось бы SCD2-измерение.
|
|
|
|
-- Шаг 1: Находим затронутые месяцы (HWM).
|
|
CREATE TEMP TABLE tmp_monthly_affected_dates ON COMMIT DROP AS
|
|
SELECT DISTINCT cal.year_actual, cal.month_actual
|
|
FROM dds.fact_flight_sales f
|
|
JOIN dds.dim_calendar cal ON f.calendar_sk = cal.calendar_sk
|
|
WHERE f._load_ts > (
|
|
SELECT COALESCE(MAX(_load_ts), '1900-01-01'::TIMESTAMP)
|
|
FROM dm.monthly_overview
|
|
);
|
|
|
|
-- Шаг 2: Уровень 1 - Агрегация фактов до рейса (flight_id).
|
|
-- Считаем точный load_factor для каждого отдельного перелета.
|
|
CREATE TEMP TABLE tmp_flight_level_metrics ON COMMIT DROP AS
|
|
SELECT
|
|
cal.year_actual,
|
|
cal.month_actual,
|
|
f.flight_id,
|
|
f.airplane_sk,
|
|
a.total_seats, -- ВНИМАНИЕ: берется текущее значение (SCD1)
|
|
COUNT(*) AS tickets_sold_per_flight,
|
|
SUM(CASE WHEN f.is_boarded THEN 1 ELSE 0 END) AS boarded_per_flight,
|
|
SUM(f.price) AS revenue_per_flight,
|
|
-- Точный load factor конкретного рейса: посаженные пассажиры / кол-во мест
|
|
(SUM(CASE WHEN f.is_boarded THEN 1 ELSE 0 END)::NUMERIC / NULLIF(a.total_seats, 0)) AS flight_load_factor
|
|
FROM dds.fact_flight_sales f
|
|
JOIN dds.dim_calendar cal ON f.calendar_sk = cal.calendar_sk
|
|
JOIN dds.dim_airplanes a ON f.airplane_sk = a.airplane_sk
|
|
-- Фильтруем только затронутые месяцы
|
|
WHERE EXISTS (
|
|
SELECT 1 FROM tmp_monthly_affected_dates tad
|
|
WHERE tad.year_actual = cal.year_actual AND tad.month_actual = cal.month_actual
|
|
)
|
|
GROUP BY cal.year_actual, cal.month_actual, f.flight_id, f.airplane_sk, a.total_seats;
|
|
|
|
-- Шаг 3: Уровень 2 - Агрегация от рейсов до месяцев.
|
|
-- Плюс собираем уникальные метрики (пассажиры, маршруты) напрямую из фактов.
|
|
CREATE TEMP TABLE tmp_monthly_overview_delta ON COMMIT DROP AS
|
|
WITH flight_aggs AS (
|
|
SELECT
|
|
year_actual,
|
|
month_actual,
|
|
airplane_sk,
|
|
COUNT(DISTINCT flight_id) AS total_flights,
|
|
SUM(tickets_sold_per_flight) AS total_tickets,
|
|
SUM(boarded_per_flight) AS total_boarded,
|
|
SUM(revenue_per_flight) AS total_revenue,
|
|
-- Честное среднее от рейсовых показателей
|
|
AVG(flight_load_factor) AS avg_load_factor
|
|
FROM tmp_flight_level_metrics
|
|
GROUP BY year_actual, month_actual, airplane_sk
|
|
),
|
|
unique_aggs AS (
|
|
SELECT
|
|
cal.year_actual,
|
|
cal.month_actual,
|
|
f.airplane_sk,
|
|
COUNT(DISTINCT r.route_bk) AS unique_routes, -- Считаем по BK (SCD2)
|
|
COUNT(DISTINCT f.passenger_sk) AS unique_passengers
|
|
FROM dds.fact_flight_sales f
|
|
JOIN dds.dim_calendar cal ON f.calendar_sk = cal.calendar_sk
|
|
JOIN dds.dim_routes r ON f.route_sk = r.route_sk
|
|
WHERE EXISTS (
|
|
SELECT 1 FROM tmp_monthly_affected_dates tad
|
|
WHERE tad.year_actual = cal.year_actual AND tad.month_actual = cal.month_actual
|
|
)
|
|
GROUP BY cal.year_actual, cal.month_actual, f.airplane_sk
|
|
)
|
|
SELECT
|
|
fa.year_actual,
|
|
fa.month_actual,
|
|
fa.airplane_sk,
|
|
a.airplane_bk,
|
|
a.model AS airplane_model,
|
|
a.total_seats,
|
|
fa.total_flights,
|
|
fa.total_tickets,
|
|
fa.total_boarded,
|
|
fa.total_revenue,
|
|
(fa.total_revenue / NULLIF(fa.total_tickets, 0))::NUMERIC(10,2) AS avg_ticket_price,
|
|
fa.avg_load_factor::NUMERIC(5,4) AS avg_load_factor,
|
|
ua.unique_routes,
|
|
ua.unique_passengers
|
|
FROM flight_aggs fa
|
|
JOIN unique_aggs ua ON fa.year_actual = ua.year_actual AND fa.month_actual = ua.month_actual AND fa.airplane_sk = ua.airplane_sk
|
|
JOIN dds.dim_airplanes a ON fa.airplane_sk = a.airplane_sk;
|
|
|
|
-- Шаг 4: UPDATE существующих записей.
|
|
UPDATE dm.monthly_overview AS tgt
|
|
SET
|
|
airplane_bk = src.airplane_bk,
|
|
airplane_model = src.airplane_model,
|
|
total_seats = src.total_seats,
|
|
total_flights = src.total_flights,
|
|
total_tickets = src.total_tickets,
|
|
total_boarded = src.total_boarded,
|
|
total_revenue = src.total_revenue,
|
|
avg_ticket_price = src.avg_ticket_price,
|
|
avg_load_factor = src.avg_load_factor,
|
|
unique_routes = src.unique_routes,
|
|
unique_passengers = src.unique_passengers,
|
|
updated_at = now(),
|
|
_load_id = '{{ run_id }}',
|
|
_load_ts = now()
|
|
FROM tmp_monthly_overview_delta AS src
|
|
WHERE tgt.year_actual = src.year_actual
|
|
AND tgt.month_actual = src.month_actual
|
|
AND tgt.airplane_sk = src.airplane_sk
|
|
AND (
|
|
tgt.total_flights IS DISTINCT FROM src.total_flights
|
|
OR tgt.total_revenue IS DISTINCT FROM src.total_revenue
|
|
OR tgt.total_boarded IS DISTINCT FROM src.total_boarded
|
|
OR tgt.unique_passengers IS DISTINCT FROM src.unique_passengers
|
|
OR tgt.airplane_model IS DISTINCT FROM src.airplane_model
|
|
);
|
|
|
|
-- Шаг 5: INSERT новых записей.
|
|
INSERT INTO dm.monthly_overview (
|
|
year_actual,
|
|
month_actual,
|
|
airplane_sk,
|
|
airplane_bk,
|
|
airplane_model,
|
|
total_seats,
|
|
total_flights,
|
|
total_tickets,
|
|
total_boarded,
|
|
total_revenue,
|
|
avg_ticket_price,
|
|
avg_load_factor,
|
|
unique_routes,
|
|
unique_passengers,
|
|
_load_id
|
|
)
|
|
SELECT
|
|
src.year_actual,
|
|
src.month_actual,
|
|
src.airplane_sk,
|
|
src.airplane_bk,
|
|
src.airplane_model,
|
|
src.total_seats,
|
|
src.total_flights,
|
|
src.total_tickets,
|
|
src.total_boarded,
|
|
src.total_revenue,
|
|
src.avg_ticket_price,
|
|
src.avg_load_factor,
|
|
src.unique_routes,
|
|
src.unique_passengers,
|
|
'{{ run_id }}' AS _load_id
|
|
FROM tmp_monthly_overview_delta AS src
|
|
WHERE NOT EXISTS (
|
|
SELECT 1 FROM dm.monthly_overview AS tgt
|
|
WHERE tgt.year_actual = src.year_actual
|
|
AND tgt.month_actual = src.month_actual
|
|
AND tgt.airplane_sk = src.airplane_sk
|
|
);
|
|
|
|
ANALYZE dm.monthly_overview;
|