-- Загрузка витрины dm.airport_traffic: инкрементальный UPSERT по датам. -- -- Учебные цели: -- 1. Обработка dual-role dimensions через UNION ALL: -- Мы превращаем один билет (факт) в два "события": вылет и прилет. -- Это позволяет собрать единую статистику аэропорта в одном проходе. -- 2. Инкремент по датам (HWM): в этой витрине метрики ограничены сутками, -- поэтому пересчитываем только те дни, где появились новые факты. -- 3. Агрегация рейсов: используем COUNT(DISTINCT flight_id) для подсчета рейсов. -- 4. Денормализация (SCD1): обновление атрибутов (город) при изменениях в измерении. -- Шаг 1: Находим даты, затронутые новыми/измененными фактами. CREATE TEMP TABLE tmp_traffic_affected_dates ON COMMIT DROP AS SELECT DISTINCT cal.date_actual AS traffic_date 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.airport_traffic ); -- Шаг 2: Unpivot (UNION ALL) и агрегация по затронутым датам. CREATE TEMP TABLE tmp_airport_traffic_delta ON COMMIT DROP AS WITH raw_events AS ( -- Роль 1: Аэропорт вылета SELECT cal.date_actual AS traffic_date, f.departure_airport_sk AS airport_sk, f.flight_id, 'departure' AS role, (CASE WHEN f.is_boarded THEN 1 ELSE 0 END) AS is_passenger, f.price AS revenue FROM dds.fact_flight_sales f JOIN dds.dim_calendar cal ON f.calendar_sk = cal.calendar_sk WHERE cal.date_actual IN (SELECT traffic_date FROM tmp_traffic_affected_dates) UNION ALL -- Роль 2: Аэропорт прилета SELECT cal.date_actual AS traffic_date, f.arrival_airport_sk AS airport_sk, f.flight_id, 'arrival' AS role, (CASE WHEN f.is_boarded THEN 1 ELSE 0 END) AS is_passenger, f.price AS revenue FROM dds.fact_flight_sales f JOIN dds.dim_calendar cal ON f.calendar_sk = cal.calendar_sk WHERE cal.date_actual IN (SELECT traffic_date FROM tmp_traffic_affected_dates) ) SELECT e.traffic_date, e.airport_sk, a.airport_bk, -- Берем из dim_airports.airport_bk a.city, -- Агрегация вылетов COUNT(DISTINCT CASE WHEN e.role = 'departure' THEN e.flight_id END) AS departures_flights, SUM(CASE WHEN e.role = 'departure' THEN e.is_passenger ELSE 0 END) AS departures_passengers, SUM(CASE WHEN e.role = 'departure' THEN e.revenue ELSE 0 END) AS departures_revenue, -- Агрегация прилетов COUNT(DISTINCT CASE WHEN e.role = 'arrival' THEN e.flight_id END) AS arrivals_flights, SUM(CASE WHEN e.role = 'arrival' THEN e.is_passenger ELSE 0 END) AS arrivals_passengers, SUM(CASE WHEN e.role = 'arrival' THEN e.revenue ELSE 0 END) AS arrivals_revenue, -- Итого: все пассажиры (сели + вышли в этом аэропорту) SUM(e.is_passenger) AS total_passengers FROM raw_events e JOIN dds.dim_airports a ON e.airport_sk = a.airport_sk GROUP BY e.traffic_date, e.airport_sk, a.airport_bk, a.city; -- Шаг 3: UPDATE существующих записей (включая денормализованные атрибуты). UPDATE dm.airport_traffic AS tgt SET airport_bk = src.airport_bk, city = src.city, departures_flights = src.departures_flights, departures_passengers = src.departures_passengers, departures_revenue = src.departures_revenue, arrivals_flights = src.arrivals_flights, arrivals_passengers = src.arrivals_passengers, arrivals_revenue = src.arrivals_revenue, total_passengers = src.total_passengers, updated_at = now(), _load_id = '{{ run_id }}', _load_ts = now() FROM tmp_airport_traffic_delta AS src WHERE tgt.traffic_date = src.traffic_date AND tgt.airport_sk = src.airport_sk AND ( tgt.total_passengers IS DISTINCT FROM src.total_passengers OR tgt.departures_flights IS DISTINCT FROM src.departures_flights OR tgt.arrivals_flights IS DISTINCT FROM src.arrivals_flights OR tgt.city IS DISTINCT FROM src.city OR tgt.airport_bk IS DISTINCT FROM src.airport_bk ); -- Шаг 4: INSERT новых записей. INSERT INTO dm.airport_traffic ( traffic_date, airport_sk, airport_bk, city, departures_flights, departures_passengers, departures_revenue, arrivals_flights, arrivals_passengers, arrivals_revenue, total_passengers, _load_id ) SELECT src.traffic_date, src.airport_sk, src.airport_bk, src.city, src.departures_flights, src.departures_passengers, src.departures_revenue, src.arrivals_flights, src.arrivals_passengers, src.arrivals_revenue, src.total_passengers, '{{ run_id }}' AS _load_id FROM tmp_airport_traffic_delta AS src WHERE NOT EXISTS ( SELECT 1 FROM dm.airport_traffic AS tgt WHERE tgt.traffic_date = src.traffic_date AND tgt.airport_sk = src.airport_sk );