- Зачем: начинающим трудно читать задания из-за необъясненных терминов и неестественных формулировок. - Что: переработаны вводная и 21 задание, названы темы разделов, объяснены термины и сокращены повторы; согласованы правила учебного текста и ссылки. - Проверка: сохранены 25 блоков SQL и команд и таблицы состава колонок; проверены 153 локальные ссылки и 32 входящие ссылки на разделы; git diff --check прошел.
153 lines
11 KiB
Markdown
153 lines
11 KiB
Markdown
# Как обновлять витрину без полной пересборки
|
||
|
||
Вы настроите обновление витрины только за дни, затронутые новыми данными.
|
||
Затем проверите, можно ли продолжить загрузку, которая успела записать
|
||
лишь часть результатов.
|
||
|
||
Факультатив повышенной сложности продолжает работу
|
||
[«Как новые заказы меняют отчет о посетителях»](identity-over-time.md).
|
||
Понадобятся ее запрос сохранения списка связей и таблица `default.lab_identity_history`.
|
||
Работайте под `student` через то же подключение DBeaver.
|
||
|
||
Стенд пересчитывает `dm.daily_traffic_v` целиком: новая связь может изменить
|
||
число известных пользователей в старом дне. Вы построите рядом свою витрину,
|
||
которая пересчитывает только затронутые дни и дает тот же результат.
|
||
|
||
При обычном продвижении модельного времени появляются новые дни сессий
|
||
и новые связи. Уже найденные пары сохраняются, а сессии прежних дней
|
||
не меняются.
|
||
Удаление данных, исправления старых сессий и пересоздание мира потребовали бы
|
||
другого способа обнаружения изменений.
|
||
|
||
## Витрина и журнал загрузок
|
||
|
||
Создайте две локальные таблицы. Если имена заняты, выберите свои.
|
||
|
||
```sql
|
||
CREATE TABLE default.lab_daily_traffic
|
||
(
|
||
report_date Date,
|
||
visitors UInt64,
|
||
known_users UInt64,
|
||
observation_day Date
|
||
)
|
||
ENGINE = ReplacingMergeTree(observation_day)
|
||
ORDER BY report_date;
|
||
|
||
CREATE TABLE default.lab_traffic_runs
|
||
(
|
||
observation_day Date
|
||
)
|
||
ENGINE = MergeTree
|
||
ORDER BY observation_day;
|
||
```
|
||
|
||
В витрине одна актуальная строка на день. Для обновления вставляйте полный
|
||
новый результат дня с новой `observation_day`. Читайте витрину с `FINAL`:
|
||
для каждого `report_date` останется строка с максимальной `observation_day`.
|
||
Старые версии могут исчезнуть при слиянии,
|
||
поэтому для воспроизведения прежнего расчета нужна сохраненная история связей.
|
||
|
||
В журнал записывайте `observation_day` списка связей, использованного
|
||
для расчета. Добавляйте дату только после записи всех выбранных дней
|
||
и успешной сверки. Максимальная дата в журнале укажет, какой список
|
||
использовался в последней завершенной загрузке.
|
||
|
||
## Первая загрузка
|
||
|
||
Убедитесь, что `world_live` стоит на паузе и нет активных прогонов генерации
|
||
и обработки. Запустите `etl_pipeline` и дождитесь успеха. Получите текущую
|
||
`observation_day` способом из первой работы. Если списка за эту дату еще нет
|
||
в вашей истории, сохраните его. Повторно вставлять уже сохраненный список
|
||
не требуется.
|
||
|
||
Напишите `INSERT INTO ... SELECT`, который рассчитывает все даты сессий из
|
||
`dds.session_v` со списком связей на текущую дату. Правила результата:
|
||
|
||
| Колонка | Что считать |
|
||
|---|---|
|
||
| `report_date` | `session_date` |
|
||
| `visitors` | число разных `client_id` |
|
||
| `known_users` | число разных известных `user_id` |
|
||
| `observation_day` | дата выбранной копии списка связей |
|
||
|
||
Сессии без известного пользователя сохраняйте в расчете. Несколько кук одного
|
||
пользователя не должны увеличивать `known_users`. Для соединения с вашей
|
||
таблицей связей используйте `GLOBAL LEFT JOIN`, отобрав одну `observation_day`.
|
||
|
||
Построчно сверьте свои `report_date`, `visitors`, `known_users` с
|
||
`dm.daily_traffic_v`. Сверка должна находить лишние и отсутствующие даты
|
||
и различия обоих показателей. Учебную витрину читайте с `FINAL`.
|
||
После совпадения запишите дату наблюдения в журнал один раз.
|
||
|
||
## Выбор дней для пересчета
|
||
|
||
Продвиньте мир на один день: `world_next_day` с `days = 1`, затем
|
||
`etl_pipeline`; после каждого запуска дождитесь успеха. Сохраните новый
|
||
список связей запросом из первой работы.
|
||
|
||
Обозначим максимальную `observation_day` в журнале **P**, а дату нового
|
||
списка связей — **H**. До записи результата составьте запрос, возвращающий
|
||
даты для пересчета без повторов. Он должен учесть два изменения:
|
||
|
||
- в сессиях появились дни, которых еще нет в вашей витрине;
|
||
- в списке на H появились пары, которых не было в списке на P; у этих `client_id`
|
||
есть сессии, в том числе за прежние дни.
|
||
|
||
Сначала выведите даты и выберите один прежний день: какая новая пара
|
||
потребовала его пересчета и какие сессии с ней связаны? Рядом посчитайте,
|
||
сколько дат пересчитала бы полная загрузка. Зафиксируйте оба числа;
|
||
равенство тоже возможно, если новые связи затронули всю историю.
|
||
|
||
Для сравнения выберите из списка на H пары, у которых день покупки
|
||
по `first_purchase_at` позже P. Совпадет ли результат с новыми парами,
|
||
найденными сравнением списков? Покажите пару, которую ограничение по дате
|
||
покупки пропускает.
|
||
|
||
## Обновление витрины
|
||
|
||
Напишите второй `INSERT INTO ... SELECT`. Для выбранных дат он должен
|
||
рассчитать все сессии с полным списком связей на H. Прибавление числа новых
|
||
связей к прежнему `known_users` не заменяет пересчет разных пользователей.
|
||
|
||
Вставьте результаты с версией H. Полностью сверьте актуальную учебную витрину
|
||
со стендовой, включая дни, которые не пересчитывали. Только после совпадения
|
||
добавьте H в журнал.
|
||
|
||
Без продвижения мира снова определите P по журналу и повторите выбор дат.
|
||
Запрос должен вернуть пустой результат, а витрина — остаться прежней.
|
||
Повторно записывать H в журнал не требуется.
|
||
|
||
## Повтор незавершенной загрузки
|
||
|
||
Снова запустите `world_next_day` с `days = 1`, затем `etl_pipeline`,
|
||
дожидаясь успеха каждого. Сохраните новый список связей с его датой H.
|
||
Получите даты для пересчета, но запишите результат только за одну из
|
||
них. Остальные пока не загружайте и дату в журнал не добавляйте.
|
||
Так вы воспроизведете остановку между записями дней, не ломая стенд.
|
||
|
||
Снова определите P по журналу, выполните запрос выбора дат и пересчет
|
||
этих дней. Проверьте, что результат совпал со стендовой витриной без очистки
|
||
уже записанных строк. После сверки запишите H в журнал.
|
||
|
||
Объясните на этом опыте, почему сохранение нового списка связей еще
|
||
не означает, что витрина обновлена. Что изменится, если записать H в журнал
|
||
до обновления витрины?
|
||
|
||
## Результат работы
|
||
|
||
Сохраните SQL первой загрузки, выбора дат, обновления и сверки.
|
||
После двух новых модельных дней ваша витрина совпадает со стендовой,
|
||
повтор без новых данных сохраняет результат, а незавершенную загрузку
|
||
можно продолжить без полной пересборки.
|
||
|
||
Сравните число пересчитанных дней с полной загрузкой и перечислите,
|
||
какие дополнительные данные пришлось хранить. Так можно оценить объем работы.
|
||
На небольшом стенде полный пересчет может быть дешевле.
|
||
В вашей реализации поиск связей пока читает всю историю покупок.
|
||
По новым данным обновляется только дневная витрина; такое обновление
|
||
называют инкрементальным.
|
||
|
||
После работы удалите свои `lab_daily_traffic`, `lab_traffic_runs` и
|
||
`lab_identity_history` через `DROP TABLE`, если они больше не нужны.
|