- Зачем:
- README и путеводитель обращаются к читателю на «вы», а ТЗ в docs/labs/
были на «ты»; после пересборки индекса смесь стала заметна.
- Что:
- во всех восьми файлах docs/labs/ местоимения, глаголы и повелительное
наклонение переведены во второе лицо множественного числа.
- названия ступеней в таблице README оставлены как в спеке.
- Проверка:
- grep по формам «ты/твой», 'ё' и 'менти' по docs/labs/ молчит;
git diff --check чистый; ревью диффа свежей сессией.
11 KiB
Задание 5: dm.utm_effectiveness
Чему учит: приставка declared_ в имени колонки — предупреждение, а не
украшение. Она говорит: это заявка клиента, а не деньги отчетности. Урок
здесь дает конфликт: суммы не сойдутся, и раскапывать почему предстоит
самому.
Правило стенда: поведение и атрибуцию считаем по трекеру, деньги — по бэкенду. Эта витрина — единственное разрешенное исключение: атрибуции по UTM у бэкенда нет, метки живут только в кликстриме. Раз считать иначе нечем, клиентская сумма берется, но называется своим именем.
Роль витрины
Эффективность рекламных каналов по дням: сколько трафика привел канал, сколько корзин и покупок в нем случилось и на какую сумму эти покупки, по словам клиента.
Атрибуция: по событию, а не по визиту
Метки едут на каждом событии, и витрина группирует события по их собственным меткам. Модели «первое касание» или «последнее касание» здесь нет: их построение — отдельная тема, а это витрина трафика по меткам.
dds.session_v при этом несет UTM первого события визита, и ответ «какой
канал привел» из сессии и из этой витрины будет разным. Это не
ошибка ни одной из сторон — это два разных вопроса, и в отчете их надо
различать.
Прямой трафик из витрины не выбрасывается
Соблазн отфильтровать строки без меток велик: витрина же про рекламу. Отфильтровать нельзя по двум причинам. Первая деловая: прямой трафик — обычно самый крупный «канал» магазина, и отчет, где его нет, врет о доле рекламы. Вторая техническая: как только часть событий выпала, итог витрины перестал сходиться с итогом источника, и проверить сборку стало нечем.
Пустые метки остаются пустыми строками, как в источнике. Своего слова для «прямого захода» витрина не изобретает: чем меньше выдуманных значений, тем меньше расшифровывать читателю.
Зерно и форма
Зерно — день события × utm_source × utm_medium × utm_campaign.
Зерно источника — событие, значит витрина материализуется:
dm.utm_effectiveness_rep / _dist, донор _stage_rep / _stage_dist,
публичное dm.utm_effectiveness_v. Ключ партиции — день.
Источник — dds.event_v, публичный договор события: витрина слоя DM читает
договор DDS, а не соседнюю витрину.
Состав колонок
| Колонка | Тип | Что это |
|---|---|---|
report_date |
Date |
день события |
utm_source |
LowCardinality(String) |
источник кампании |
utm_medium |
LowCardinality(String) |
канал кампании |
utm_campaign |
String |
имя кампании |
visitors |
UInt64 |
число разных client_id |
pageviews |
UInt64 |
просмотров страниц |
add_to_carts |
UInt64 |
добавлений в корзину |
purchases |
UInt64 |
покупок |
declared_revenue |
Decimal(18, 2) |
выручка покупок глазами клиента |
_load_id |
String |
запуск, собравший день |
_load_ts |
DateTime64(3, 'UTC') |
когда собран |
declared_revenue складывает purchase_revenue[1] событий purchase.
Индекс не описка: блок purchase* едет параллельными массивами, и у события
покупки в них ровно один элемент — контракт источника обещает один номер
заказа и одну объявленную сумму на событие. Тем же индексом берет свою
сторону стендовая сверка purchase_vs_orders.
Приводить тип не нужно: в договоре purchase_revenue уже
Array(Decimal(18, 2)). Float64 эта сумма была только в выгрузке
источника, и ODS перевел ее в точные деньги на приеме — сложение Decimal
точное, и где стоит CAST, до или после sum, на результат не влияет.
Витрина просто хранит то, что сложила, в Decimal(18, 2).
utm_campaign оставлена обычной строкой: имен кампаний со временем
становится много, и словарь LowCardinality на растущем множестве
перестает окупаться.
Ритм пересборки
Источник — события: день замерзает сразу. Объем прогона и механика те же, что в задании 2: хвост несобранных дней, донор, замена дневных партиций.
Ключ шардирования
Ваше решение по правилу донора, «Раскладка по шардам». Зерно здесь составное, из трех колонок, и решение «взять хеш от одной из них» надо защищать отдельно: назовите, какая доля строк придется на самое частое значение выбранной колонки.
Конфликт, ради которого витрина и написана
Соберите за один и тот же день две суммы: sum(declared_revenue) из своей
витрины и sum(revenue) из dm.revenue_daily_v. Они не сойдутся, и это
верное поведение обеих витрин.
Базы у двух сумм при этом сравнимые: revenue_daily складывает цену позиций
на количество и ни скидки, ни доставки в себя не берет — ровно то, что видит
клиент. Значит все расхождение до копейки объясняется тем, что случилось с
заказом, а не тем, как считали деньги. Ищите ответы среди таких причин:
- отмененный заказ из кликстрима никуда не девается: событие покупки
случилось, а
revenue_dailyсчитает только оплаченные; - событие покупки может потеряться по дороге, а заказ при этом доехать — и наоборот, событие приехать дважды;
- клиент назвал сумму, отличную от серверной суммы позиций: генератор делает это намеренно у одного-двух заказов из ста.
Как эти же расхождения выглядят на зерне заказа, показывает стендовая
витрина dm.purchase_vs_orders_v с ее классами расхождений: это готовый
ответ на вопрос «а сколько именно и из-за чего». Запишите свой вывод —
в двух-трех фразах, своими словами.
Проверять сходимость этих двух сумм не нужно и нельзя: они и не должны сходиться.
Как проверить себя
Деловой ключ — тройка utm_source, utm_medium, utm_campaign внутри
дня. Соберите прямой агрегат из dds.event_v за день, соедините с
dm.utm_effectiveness_v полным внешним соединением и посчитайте строки, где
ключа нет с одной из сторон или где расходится любая из пяти величин. Ноль —
зеленый.
Две проверки покрупнее ловят самые частые ошибки:
- сумма
pageviews,add_to_cartsиpurchasesза день равна числу событий соответствующего типа вdds.event_vза тот же день; - сумма
declared_revenueза день равна суммеpurchase_revenue[1]по всем событиямpurchaseэтого дня.
Обе не сойдутся, если прямой трафик отфильтрован, и разойдутся ровно на его
долю — по этому следу отфильтрованные строки и находят. visitors, как и в
задании 3, по строкам не складывается: один посетитель приходит по разным
меткам.
Приемка
- пять объектов витрины созданы, донор объявлен через
ASи шардируется ключом цели; - строки без UTM-меток в витрине остались;
declared_revenueхранится вDecimal(18, 2);- расхождение с
dm.revenue_daily_vобъяснено письменно; - задача
utm_effectivenessвhomework_checkзеленая; - задача витрины добавлена в
dm_transform.