docs(labs): переписана проза пяти ТЗ ступени 1 и раздел «Как принято»
- Зачем:
- владелец при чтении забраковал прозу ТЗ ступени 1: образы вместо
утверждений, заголовки-метафоры, договоренности стенда поданы как
законы. Тикет #228.
- Что:
- пять ТЗ (задания 1–5) написаны заново по форме ТЗ ступени 2:
содержание, колонки, формулы, самопроверка и приемка сохранены.
- раздел README лаб «Общие правила ступени 1» переименован в «Как принято
на ступени 1» и переписан как выбор с доводом; точечно поправлены
«Самопроверка» и таблица ступени 1.
- Проверка:
- grep по «ё», «менти», формам «ты» и словам-законам по docs/labs/*.md
молчит (единственное «правило» — в нетронутом разделе «Уборка»).
- скрипт ссылок и якорей из #226 печатает пусто; git diff --check чист.
- сверка имен в обратных кавычках и чекбоксов старых и новых файлов —
без расхождений.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
dc2cbc99dc
commit
29a01f0016
6 files changed
+472
-522
No files matched your search
@@ -1,114 +1,93 @@
|
||||
# Задание 1: `dm.events_enriched_v`
|
||||
|
||||
**Чему учит:** слой — это контракт, а не копия данных. Витрина, которая не
|
||||
меняет зерно источника, остается представлением, и копии событий ради
|
||||
других имен колонок не заводит ни один боевой склад.
|
||||
**Чему учит:** строить слой как договор о данных, а не как их копию.
|
||||
Оставлять витрину представлением, если зерно источника не меняется.
|
||||
|
||||
Это разминка. Строк она не хранит, в конвейер не встает, ключа шардирования
|
||||
у нее нет. Задача одна: собрать плоское событие для BI и не поддаться
|
||||
соблазну разложить его на диск.
|
||||
## Сначала прочитать
|
||||
|
||||
- [«Хранилище и его слои»](../handbook/storage-layers.md).
|
||||
|
||||
## Роль витрины
|
||||
|
||||
Договор события в DDS широкий — 44 колонки, почти весь формат выгрузки:
|
||||
часть из них технические координаты источника, часть — параллельные массивы
|
||||
товаров. Аналитику в Superset нужен плоский набор деловых полей и пара
|
||||
производных, которые иначе каждый пишет заново в каждом запросе.
|
||||
В `dds.event_v` 44 колонки, включая технические поля источника и массивы
|
||||
товаров. Для анализа событий в Superset нужны деловые поля и три готовых
|
||||
значения: час события, путь страницы и домен перехода. Тогда вам не придется
|
||||
повторять эти выражения в каждом запросе.
|
||||
|
||||
Витрина отвечает на вопрос «что вообще происходило»: с нее начинают, когда
|
||||
надо посмотреть на события глазами человека, а не разбирать широкую строку
|
||||
источника.
|
||||
Витрины следующих заданий читают DDS напрямую. Использовать
|
||||
`dm.events_enriched_v` как источник для них не нужно: задания независимы, их
|
||||
можно выполнять в любом порядке.
|
||||
|
||||
Источником для витрин следующих заданий она не служит. Витрина слоя DM
|
||||
читает публичный договор DDS — так устроены и стендовые витрины, и потому
|
||||
задания независимы: соседнюю домашку можно делать первой, а можно не делать
|
||||
вовсе.
|
||||
## Что сделать
|
||||
|
||||
## Зерно и форма
|
||||
Создайте обычное представление `CREATE VIEW dm.events_enriched_v`.
|
||||
Единственный источник — `dds.event_v`. Зерно — **событие**: каждой строке
|
||||
источника соответствует одна строка витрины.
|
||||
|
||||
Зерно — **событие**: одна строка витрины равна одной строке `dds.event_v`.
|
||||
Зерно источника сохранено, значит форма — обычное представление
|
||||
`CREATE VIEW dm.events_enriched_v`, без физической таблицы, без донора и
|
||||
без задачи в `dm_transform`. Пересобирать нечего.
|
||||
Физическая таблица, таблица-донор и задача в `dm_transform` не нужны.
|
||||
Представление не хранит строки, поэтому ключ шардирования и пересборка ему
|
||||
не требуются.
|
||||
|
||||
Источник — `dds.event_v`, и только он.
|
||||
Укажите колонки явно. Иначе новая колонка источника попадет в витрину
|
||||
автоматически, и договор с потребителем изменится без вашего участия.
|
||||
|
||||
## Состав колонок
|
||||
| Группа | Колонки |
|
||||
|---|---|
|
||||
| Время и посетитель | `event_date`, `event_time`, `event_hour`, `client_id`, `event_id` |
|
||||
| Действие | `event_type` (`pageview`, `add_to_cart`, `purchase`), `url`, `page_path`, `referer`, `referer_host`, `title` |
|
||||
| UTM-метки | `utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term` |
|
||||
| Устройство и регион | `device_category`, `browser`, `operating_system`, `region_country`, `region_city` |
|
||||
| Товары и покупка | `product_id`, `product_name`, `product_category`, `product_price`, `product_quantity`, `product_event_type`, `purchase_id`, `purchase_revenue`, `purchase_coupon` |
|
||||
|
||||
Явный список обязателен: договор не должен расширяться сам, когда в
|
||||
источник добавят колонку.
|
||||
Три колонки вычислите в представлении. Остальные возьмите из источника без
|
||||
изменений, сохранив массивы `product_*`.
|
||||
|
||||
**Время и посетитель:** `event_date`, `event_time`, `event_hour`,
|
||||
`client_id`, `event_id`.
|
||||
|
||||
**Действие:** `event_type` (`pageview`, `add_to_cart`, `purchase`), `url`,
|
||||
`page_path`, `referer`, `referer_host`, `title`.
|
||||
|
||||
**Атрибуция:** `utm_source`, `utm_medium`, `utm_campaign`, `utm_content`,
|
||||
`utm_term`.
|
||||
|
||||
**Паспорт визита:** `device_category`, `browser`, `operating_system`,
|
||||
`region_country`, `region_city`.
|
||||
|
||||
**Торговый блок, массивами как в источнике:** `product_id`, `product_name`,
|
||||
`product_category`, `product_price`, `product_quantity`,
|
||||
`product_event_type`, `purchase_id`, `purchase_revenue`, `purchase_coupon`.
|
||||
|
||||
Три колонки в источнике не лежат и считаются здесь:
|
||||
|
||||
| Колонка | Как считается | Зачем |
|
||||
| Колонка | Как считается | Для чего нужна |
|
||||
|---|---|---|
|
||||
| `event_hour` | `toHour(event_time)` | суточный профиль спрашивают в каждом отчете |
|
||||
| `page_path` | путь `url` без домена и параметров | группировка по странице без разбора строки в каждом запросе |
|
||||
| `referer_host` | домен `referer` | «откуда пришли» одним значением, а не полным адресом |
|
||||
| `event_hour` | `toHour(event_time)` | Анализ событий по часам суток |
|
||||
| `page_path` | путь `url` без домена и параметров | Группировка по странице |
|
||||
| `referer_host` | домен `referer` | Анализ источников перехода |
|
||||
|
||||
Служебных `_load_id` и `_load_ts` у витрины нет, и это не забывчивость. Их
|
||||
нет и у `dds.event_v`: событие в DDS не грузят, а читают из ODS, поэтому
|
||||
координат собственной загрузки у него не бывает. Служебная колонка называет
|
||||
запуск, который положил строку на диск, — здесь на диск никто ничего не
|
||||
кладет.
|
||||
Не добавляйте `_load_id` и `_load_ts`. Их нет и у `dds.event_v`: события в
|
||||
DDS читают из ODS без отдельной загрузки. Эти служебные поля указывают на
|
||||
запуск, который записал строку на диск. Представление ничего не загружает.
|
||||
|
||||
## Ловушка задания
|
||||
## Типичная ошибка
|
||||
|
||||
Массивы `product_*` тянет развернуть через `ARRAY JOIN` — так плоская
|
||||
таблица становится по-настоящему плоской. Разворачивать нельзя, и вреда
|
||||
здесь два.
|
||||
Не разворачивайте массивы товаров через `ARRAY JOIN`. У события с тремя
|
||||
позициями получится три строки. Зерно изменится. Витрину с новым зерном на
|
||||
стенде хранят готовым агрегатом — причины описаны в разделе [«Как принято на
|
||||
ступени 1»](README.md#как-принято-на-ступени-1).
|
||||
|
||||
Очевидный: у события с тремя позициями получилось бы три строки, зерно
|
||||
витрины перестало бы совпадать с зерном источника, а вместе с ним развалилось
|
||||
бы и правило формы — такую витрину уже пришлось бы материализовать.
|
||||
Главная опасность — потеря событий с пустым массивом товаров: `ARRAY JOIN`
|
||||
исключает их из результата. Товары есть только в событиях корзины и покупки.
|
||||
Поэтому исчезнет большинство событий — девятнадцать строк из двадцати.
|
||||
Запрос выполнится без ошибки, а витрина по имени и составу колонок
|
||||
по-прежнему будет выглядеть таблицей событий.
|
||||
|
||||
Неочевидный и куда более разрушительный: `ARRAY JOIN` — соединение
|
||||
внутреннее, и строку с пустым массивом он выбрасывает. Торговый блок пуст у
|
||||
всех событий, кроме корзин и покупок, то есть у подавляющего большинства.
|
||||
Так «плоская таблица событий» превращается в таблицу товарных позиций и
|
||||
теряет девятнадцать строк из двадцати — а по имени и по составу колонок она
|
||||
по-прежнему выглядит таблицей событий. Ошибка не падает и ничего не
|
||||
подсвечивает.
|
||||
|
||||
Массивы едут в договор как есть; разворачивает их тот, кому нужна позиция
|
||||
заказа, — и делает это уже в своей витрине, с другим зерном и другим именем.
|
||||
Сохраните массивы как в источнике. Для анализа отдельных товарных позиций
|
||||
нужна другая витрина с другим зерном и именем.
|
||||
|
||||
## Как проверить себя
|
||||
|
||||
Деловой ключ — `event_id` внутри дня. Соберите прямой ответ из
|
||||
`dds.event_v` за тот же день, соедините с витриной полным внешним
|
||||
соединением и посчитайте строки, где ключа нет с одной из сторон или где
|
||||
различаются значения деловых колонок. Ноль — зеленый.
|
||||
Деловой ключ — `event_id` внутри дня. Получите результат прямо из
|
||||
`dds.event_v` за тот же день. Соедините его с витриной полным внешним
|
||||
соединением по `event_id`. Посчитайте строки, где ключ отсутствует с одной
|
||||
из сторон или различаются значения деловых колонок. Расхождений должно быть
|
||||
ноль.
|
||||
|
||||
Отдельно проверяются три производные колонки: `event_hour`, `page_path` и
|
||||
`referer_host` должны совпасть с тем же выражением, посчитанным по
|
||||
`dds.event_v` на лету.
|
||||
Отдельно сверьте `event_hour`, `page_path` и `referer_host` с теми же
|
||||
выражениями, рассчитанными прямо по `dds.event_v`.
|
||||
|
||||
Если строк в витрине оказалось в разы меньше, чем в источнике, а остались в
|
||||
ней одни корзины и покупки — это развернутые массивы: `ARRAY JOIN` выбросил
|
||||
все события с пустым торговым блоком.
|
||||
Если строк в витрине в разы меньше, чем в источнике, и остались только
|
||||
события корзины и покупки, это признак развернутых массивов: `ARRAY JOIN`
|
||||
исключил все события с пустым массивом товаров.
|
||||
|
||||
## Приемка
|
||||
|
||||
- [ ] `dm.events_enriched_v` создано, физических таблиц под ним нет;
|
||||
- [ ] список колонок в представлении явный;
|
||||
- [ ] представление `dm.events_enriched_v` создано без физических таблиц;
|
||||
- [ ] список колонок в представлении задан явно;
|
||||
- [ ] число строк и набор `event_id` совпадают с `dds.event_v` за каждый
|
||||
прожитый день;
|
||||
- [ ] задача `events_enriched` в `homework_check` зеленая;
|
||||
- [ ] задачи в `dm_transform` **нет** — и не должно быть.
|
||||
- [ ] задача `events_enriched` в `homework_check` завершилась успешно;
|
||||
- [ ] задачи для этой витрины в `dm_transform` нет.
|
||||
Reference in new issue
Block a user