-- DDS: объём пересборки сессий — какие дни собирает этот запуск. -- -- Ответ — две даты, границы непрерывного отрезка; список дней между ними -- разворачивает даг. Пустой ответ означает пустой источник. Пустой первый -- день — что собирать нечего: все прожитые дни уже в слое, и прогон честно -- пропускает сборку. -- -- Файл кончается без точки с запятой: почему — sql/dds/order_scope.sql. -- -- Правый край — последний прожитый день, и знает его только позиция мира. -- События живой день от прожитого не отличают: и посреди дня D, и сразу после -- его конца max(EventDate) = D. А разница несущая — визит, начатый в 23:50, -- посреди дня ещё идёт, и собранная сессия оказалась бы обрезанной навсегда: -- пересобирать замёрзший день слой не станет -- (docs/architecture/dds/session.md, «Движок и наполнение»). -- -- Позиция — номер первого несыгранного дня, счётчик прожитого. Своей копии -- начала оси стенд из-за неё не заводит: начало он спрашивает у данных, и -- дата дня N в них — константа, начало оси плюс N. Держится якорь на том, что -- ранние дни из ods.event не исчезают: срок жизни назначен сырью и таблицам -- ошибок, самому событию — нет (sql/ddl/20-ods-tables.sql). -- -- За «доехал» позиция не отвечает — только за «прожит». Дождаться хвоста -- потока — работа отдельного шага готовности источника, и в даге его пока нет -- (#156): до него недожёванный хвост даёт замёрзший недособранный день -- (docs/architecture/etl.md, «Готовность источника»). WITH (SELECT min(EventDate) FROM ods.event_dist) AS first_source_day, first_source_day + ({position:UInt16} - 1) AS last_lived_day, -- Хвост считается от первой дыры, а не от последнего собранного дня: -- замены партиций падают порознь, и счёт по максимуму собранного дня -- дыру позади себя не заметил бы (доводы целиком — sql/dds/order_scope.sql). -- У сессий это единственный способ дыру найти: дышащего окна, которое -- пересобрало бы день заодно, здесь нет. -- -- GLOBAL обязателен: обе таблицы распределённые, и обычный NOT IN сверял -- бы дни источника с локальной частью слоя на каждом шарде. -- minOrNull отличает «дыр нет» от даты по умолчанию. ( SELECT minOrNull(day) FROM (SELECT DISTINCT EventDate AS day FROM ods.event_dist) WHERE day GLOBAL NOT IN (SELECT session_date FROM dds.session_dist) ) AS first_missing_day SELECT -- Живой день в слое отсутствует всегда, и работой это не считается: -- дырой он станет, только когда мир его проживёт. if(first_missing_day <= last_lived_day, first_missing_day, NULL) AS first_day, last_lived_day AS last_day -- FINAL не нужен: повтор доставки не двигает ни край оси, ни набор дней. WHERE (SELECT count() FROM ods.event_dist) > 0