# Как обновлять витрину без полной пересборки **Чему учит:** выбирать дни для пересчета по новым данным, проверять результат и повторять незавершенную загрузку. Факультатив повышенной сложности продолжает работу [«Как новые заказы меняют отчет о посетителях»](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; у этих кук есть сессии, в том числе за прежние дни. Сначала выведите даты и выберите один прежний день: какая новая пара потребовала его пересчета и какие сессии с ней связаны? Рядом посчитайте, сколько дат пересчитала бы полная загрузка. Зафиксируйте оба числа; равенство тоже возможно, если новые связи затронули всю историю. Для сравнения выберите из списка на 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`, если они больше не нужны.