-- Загрузка DDS dim_routes: SCD2 с hashdiff. -- Учебный комментарий: Используем TEMP TABLE для подготовки дельты. -- Это избавляет от дублирования логики hashdiff в UPDATE и INSERT блоках. CREATE TEMP TABLE tmp_routes_src ON COMMIT DROP AS SELECT route_no, departure_airport, arrival_airport, airplane_code, days_of_week, departure_time, duration, md5( COALESCE(departure_airport, '') || '|' || COALESCE(arrival_airport, '') || '|' || COALESCE(airplane_code, '') || '|' || COALESCE(days_of_week::TEXT, '') || '|' || COALESCE(departure_time::TEXT, '') || '|' || COALESCE(duration::TEXT, '') ) AS hashdiff, ROW_NUMBER() OVER (PARTITION BY route_no ORDER BY validity DESC) AS rn FROM ods.routes; -- Statement 1: Закрыть устаревшие версии (valid_to = текущая дата). UPDATE dds.dim_routes AS d SET valid_to = CURRENT_DATE, updated_at = now(), _load_id = '{{ run_id }}', _load_ts = now() FROM tmp_routes_src AS s WHERE s.rn = 1 AND d.route_bk = s.route_no AND d.valid_to IS NULL AND d.hashdiff <> s.hashdiff; -- Statement 1.1: Закрыть "исчезнувшие" маршруты. UPDATE dds.dim_routes AS d SET valid_to = CURRENT_DATE, updated_at = now(), _load_id = '{{ run_id }}', _load_ts = now() WHERE d.valid_to IS NULL AND NOT EXISTS ( SELECT 1 FROM tmp_routes_src AS s WHERE s.rn = 1 AND s.route_no = d.route_bk ); -- Statement 2: Вставить новые версии (для изменённых и новых route_no). WITH max_sk AS ( -- Учебный комментарий: Генерация SK через MAX() + ROW_NUMBER() -- Этот подход работает безопасно только потому, что Airflow запускает -- джобы загрузки для одной таблицы строго последовательно (concurrency=1). SELECT COALESCE(MAX(route_sk), 0) AS v FROM dds.dim_routes ) INSERT INTO dds.dim_routes ( route_sk, route_bk, departure_airport, arrival_airport, airplane_code, days_of_week, departure_time, duration, hashdiff, valid_from, valid_to, created_at, updated_at, _load_id, _load_ts ) SELECT (SELECT v FROM max_sk) + ROW_NUMBER() OVER (ORDER BY s.route_no)::INTEGER, s.route_no, s.departure_airport, s.arrival_airport, s.airplane_code, s.days_of_week, s.departure_time, s.duration, s.hashdiff, CASE WHEN EXISTS ( SELECT 1 FROM dds.dim_routes AS d2 WHERE d2.route_bk = s.route_no ) THEN CURRENT_DATE ELSE '1900-01-01'::DATE END AS valid_from, NULL, now(), now(), '{{ run_id }}', now() FROM tmp_routes_src AS s WHERE s.rn = 1 AND NOT EXISTS ( SELECT 1 FROM dds.dim_routes AS d WHERE d.route_bk = s.route_no AND d.valid_to IS NULL AND d.hashdiff = s.hashdiff ); ANALYZE dds.dim_routes;