docs(warehouse): упрощены объяснения витрин, сессий и пересчета
Зачем: читателю ступеней 1–4 нужно проследить выбор дней, расчет строки и публикацию результата без расшифровки метафор. Что: согласованы справочники DM, сессий, ETL и два раздела хранилища с комментариями шести SQL-примеров. Уточнены ограничения повторного запуска и выбора атрибутов при равном времени событий. Проверка: make lint, git diff --check, локальные Markdown-ссылки и якоря. SQL без комментариев совпадает; AST Python без строк документации совпадает. Исторические разделы и материалы вне границ задачи сохранены. Refs: #249
This commit is contained in:
1 parent
84dc718f62
commit
5f317ba8d2
11 files changed
+440
-490
No files matched your search
+21
-42
@@ -1,35 +1,17 @@
|
||||
-- DDS: нарезка событий на визиты и сборка дней в таблицу-двойник.
|
||||
-- DDS: сборка сессий выбранных дней в донор dds.session_stage_dist.
|
||||
-- Донор очищается целиком; одна вставка собирает весь диапазон.
|
||||
--
|
||||
-- Двойник очищается целиком в начале, дни собираются одной вставкой на весь
|
||||
-- отрезок — по правилу заказа (sql/dds/order_rebuild.sql).
|
||||
-- Первое окно помечает событие единицей, если с предыдущего события того же
|
||||
-- посетителя за тот же день прошло больше 1800 секунд. Второе окно суммирует
|
||||
-- эти единицы: получается номер визита внутри посетителя и дня.
|
||||
-- PARTITION BY (client_id, event_date) разделяет визиты по модельной полночи.
|
||||
-- Учитываются все типы событий.
|
||||
--
|
||||
-- Нарезка — центральный расчёт слоя, и стоит она на двух окнах подряд.
|
||||
-- Первое спрашивает у каждого события, сколько прошло с предыдущего события
|
||||
-- той же куки в тот же день, и ставит единицу там, где пауза длиннее
|
||||
-- таймаута: это шов между визитами. Второе считает нарастающий итог этих
|
||||
-- единиц — номер визита внутри дня куки. Дальше визит становится обычной
|
||||
-- группировкой.
|
||||
--
|
||||
-- Границу суток окна не проверяют: она уже в разбиении PARTITION BY
|
||||
-- (client_id, event_date), поэтому визит через полночь не склеится и при
|
||||
-- короткой паузе. Так правило визита ложится в запрос целиком: одна кука,
|
||||
-- один день, паузы не длиннее таймаута (CONTEXT.md, «Визит»).
|
||||
--
|
||||
-- Первое событие куки за день сравнивается само с собой — третий аргумент
|
||||
-- lagInFrame. Паузы у него нет, шов ему не нужен, и номер первого визита
|
||||
-- получается нулём: номера различают визиты внутри дня куки, а не считают их
|
||||
-- от единицы.
|
||||
--
|
||||
-- Порядок в окнах полный — метка времени и event_id. Окна считаются двумя
|
||||
-- разными сортировками (видно в EXPLAIN PIPELINE), и при равных метках они
|
||||
-- вправе разойтись: шов достался бы одной строке, а нарастающий итог другой,
|
||||
-- и сессия разъехалась бы надвое.
|
||||
--
|
||||
-- Тридцать минут написаны числом, а не приехали параметром: это правило
|
||||
-- аналитики, а не константа мира. Генератор режет визиты тем же порогом;
|
||||
-- разойдись эти два числа — увидит счётная сверка с VisitID
|
||||
-- (docs/architecture/dds/session.md, «Сверка с эталоном»).
|
||||
|
||||
-- У первого события lagInFrame возвращает его собственное время: разница
|
||||
-- равна нулю, opens_visit = 0, и первый visit_no тоже равен нулю.
|
||||
-- Оба окна используют (event_time, event_id), чтобы при равном времени
|
||||
-- признак начала и накопленный номер визита относились к одним событиям.
|
||||
-- Порог 1800 секунд — правило визита; он не передается параметром.
|
||||
ALTER TABLE dds.session_stage_rep ON CLUSTER clickstream_cluster DROP PARTITION ALL;
|
||||
|
||||
INSERT INTO dds.session_stage_dist
|
||||
@@ -93,8 +75,8 @@ WITH
|
||||
FROM seams
|
||||
)
|
||||
SELECT
|
||||
-- Чекан визита: кука и его начало. Время входит в хеш секундой эпохи,
|
||||
-- поэтому id держится за содержание сессии, а не за порядок сборки.
|
||||
-- Идентификатор зависит от посетителя и начала визита.
|
||||
-- Повторная сборка тех же событий дает тот же session_id.
|
||||
cityHash64(client_id, min(event_time)),
|
||||
client_id,
|
||||
event_date,
|
||||
@@ -107,8 +89,10 @@ SELECT
|
||||
countIf(event_type = 'purchase'),
|
||||
argMin(url, event_time),
|
||||
argMax(url, event_time),
|
||||
-- Атрибуция и паспорт куки берутся с первого события визита. У паспорта
|
||||
-- любое событие дало бы то же: устройство и город у куки постоянны.
|
||||
-- UTM, устройство и город берутся по минимальному event_time.
|
||||
-- Как и у страниц выше, при равном времени выбор между событиями
|
||||
-- не определен: event_id в эти argMin/argMax не входит.
|
||||
-- Устройство и город на стенде постоянны для одной куки.
|
||||
argMin(utm_source, event_time),
|
||||
argMin(utm_medium, event_time),
|
||||
argMin(utm_campaign, event_time),
|
||||
@@ -120,12 +104,7 @@ SELECT
|
||||
now64(3, 'UTC')
|
||||
FROM numbered
|
||||
GROUP BY client_id, event_date, visit_no
|
||||
-- Капкана parallel_distributed_insert_select здесь нет, и это выигрыш
|
||||
-- ко-локации: ключ шардирования у источника и цели один — cityHash64 от
|
||||
-- куки, — поэтому визит ляжет туда же, где лежали его события, исполнись
|
||||
-- вставка хоть на инициаторе, хоть локально на шардах. Заказу приходится
|
||||
-- называть ноль вслух: совпадение ключей у него случайное
|
||||
-- (sql/dds/order_rebuild.sql). Здесь выбор сделан обратный и он тоже
|
||||
-- осознанный: настройку не называть, чтобы не притворяться, будто её значение
|
||||
-- что-то решает.
|
||||
-- Источник и цель распределены по cityHash64(client_id): локальная вставка
|
||||
-- сохраняет нужную раскладку, parallel_distributed_insert_select менять
|
||||
-- не требуется. Перед заменой партиций ждем доставки строк на шарды.
|
||||
SETTINGS distributed_foreground_insert = 1;
|
||||
Reference in new issue
Block a user