diff --git a/docs/labs/optional/duplicate-purchases.md b/docs/labs/optional/duplicate-purchases.md new file mode 100644 index 0000000..38f860e --- /dev/null +++ b/docs/labs/optional/duplicate-purchases.md @@ -0,0 +1,65 @@ +# Почему повтор покупки завышает выручку + +**Чему учит:** отличать повторную доставку строки от повторного события об +одной покупке и выбирать ключ, по которому считать деньги. + +Факультатив доступен после ступени 1. Работайте в DBeaver пользователем +`analyst` по [инструкции](../../../README.md#подключиться-из-dbeaver). +Дождитесь завершения `etl_pipeline`; оставьте `world_live` на паузе и +дождитесь уже начатых прогонов, чтобы данные не менялись между запросами. + +## Посчитайте клиентскую сумму двумя способами + +Возьмите один закрытый день: его дата должна быть не позднее +`max(snapshot_date) - 6` из `ods.order_v`. Во всех запросах к `dds.event_v` +отбирайте этот `event_date` и `event_type = 'purchase'`. + +Номер заказа находится в `purchase_id[1]`, заявленная сумма — в +`purchase_revenue[1]`. По формату источника у события покупки один номер заказа. + +Сначала посчитайте число событий и сумму всех заявленных сумм. Затем найдите +номера заказов, которые встречаются несколько раз. Если таких нет, выберите +другой закрытый день. + +Для двух повторившихся покупок выпишите `event_id`, `client_id`, `event_time`, +номер заказа и сумму. Сопоставьте число событий и заказов в `dds.order_v`. + +Теперь рассчитайте клиентскую сумму, учитывая каждый номер заказа один раз. +До выбора одной суммы на заказ проверьте, что во всех его событиях она +одинакова. Объясните, почему удалять одинаковые денежные значения вместо +повторных номеров заказов было бы ошибкой. + +## Объясните разницу + +Для каждого повторившегося заказа определите число лишних событий и их +вклад в завышение. Сложите эти вклады и сверьте с разностью двух расчетов. +Равенство должно выполняться до копейки. + +Слой ODS убирает повторную доставку одного события по его идентификатору; +`dds.event_v` читает уже это состояние. Объясните по своим строкам, почему +повтор покупки сохранился. Какие поля совпадают и какие различаются? +Какой ключ отвечает на вопрос «это то же событие», а какой — «это тот же заказ»? + +После собственного расчета найдите эти заказы в `dm.purchase_vs_orders_v`. +Если у повторившегося заказа класс отличается от `duplicate_event`, проверьте +его статус и [порядок присвоения классов](../../architecture/dm.md#dmpurchase_vs_orders_v). +Класс витрины не заменяет поиск всех повторов в событиях. + +## Сравните с выручкой магазина + +Сложите `revenue` из `dm.revenue_daily_v` за тот же `report_date` по всем +категориям. Эта витрина считает стоимость позиций оплаченных заказов, +без скидки и доставки. + +Сравните результат с исправленной клиентской суммой. Совпадение не требуется: +удаление повторов устраняет одну причину расхождения. Найдите хотя бы один +заказ, который объясняет, почему деньги трекера и магазина все еще могут +различаться. Для этого сопоставьте события, заказ и готовую сверку. + +## Что должно получиться + +- [ ] сохранены две клиентские суммы и строки повторившихся покупок; +- [ ] разность сумм полностью объяснена вкладом лишних событий; +- [ ] выбор ключа для устранения повторов подтвержден данными; +- [ ] вы можете объяснить, почему устранение повторов не завершает сверку + выручки с магазином. diff --git a/docs/labs/optional/missing-purchases.md b/docs/labs/optional/missing-purchases.md new file mode 100644 index 0000000..75528d3 --- /dev/null +++ b/docs/labs/optional/missing-purchases.md @@ -0,0 +1,79 @@ +# Куда пропали покупки + +**Чему учит:** обнаружить потерю событий при сверке двух источников и +обосновать, каким данным можно доверять при подсчете покупок. + +Факультатив доступен после ступени 1. В DBeaver подключитесь пользователем +`analyst` по [инструкции](../../../README.md#подключиться-из-dbeaver). +Дождитесь завершения `etl_pipeline`; на время расследования оставьте +`world_live` на паузе и дождитесь уже начатых прогонов. + +## Выберите день + +Магазин зарегистрировал заказы, но часть событий покупки не дошла до трекера. +Найдите след этой потери сначала в событиях сайта, затем в заказах. + +Возьмите день, для которого уже получен последний слепок семидневного окна. +Запрос показывает самую позднюю подходящую дату: + +```sql +SELECT max(snapshot_date) - 6 AS last_closed_day +FROM ods.order_v; +``` + +Используйте выбранный день во всех запросах. События отбирайте по +`dds.event_v.event_date`, заказы — по `dds.order_v.order_date`. +Сравнивайте все заказы дня, включая отмененные: сейчас вопрос в том, +зарегистрировал ли магазин заказ, а не в том, был ли он оплачен. + +## Найдите разрыв в событиях + +Посчитайте просмотры страницы подтверждения и события покупки: + +- у просмотра `event_type = 'pageview'`, а `url` заканчивается на `/confirmation`; +- у покупки `event_type = 'purchase'`. + +Запишите оба числа и разность. Какую потерю она позволяет заподозрить? +Достаточно ли этих двух чисел, чтобы назвать пропавшие покупки? + +## Сверьте с магазином + +В событии номер заказа находится в `purchase_id[1]`, в заказах — в `order_id`. +Соберите список заказов выбранного дня, для которых нет события покупки. +Несколько событий с одним номером заказа должны оставаться одной покупкой. + +Проверьте два равенства: + +- число заказов магазина равно числу разных заказов из событий плюс число + заказов без события; +- разность просмотров подтверждения и событий покупки равна числу заказов + без события. + +Первое равенство проверяет полноту сопоставления источников. Второе относится +к устройству этого стенда. Найдите покупку с повторным событием и посмотрите +соседние события того же `client_id` по времени: что произошло со страницей +подтверждения? Объясните, почему повтор не скрыл разрыв воронки. + +Если в выбранном дне повторов нет, возьмите другой закрытый день. +Для оценки доли потерь используйте число заказов магазина и объясните, +почему число просмотров подтверждения может дать другую долю. + +## Сопоставьте вывод с готовой сверкой + +Найдите свои заказы без события в `dm.purchase_vs_orders_v` по `order_id`. +Сравните их `status` и `mismatch_class`. Витрина присваивает заказу один класс, +хотя причин расхождения может быть несколько. По +[правилу классификации](../../architecture/dm.md#dmpurchase_vs_orders_v) +объясните, почему потеря события не всегда попадает в `lost_event`. + +Возьмите один заказ из своего списка и сохраните два запроса: первый находит +его в магазине, второй показывает отсутствие покупки в трекере. Сформулируйте, +какой источник нужен для отчета о поведении посетителей, а какой — для денег. + +## Что должно получиться + +- [ ] сохранены день, счетчики воронки и список заказов без события; +- [ ] оба равенства сходятся, влияние повторного просмотра объяснено; +- [ ] доля потерь посчитана с обоснованным знаменателем; +- [ ] один случай подтвержден запросами к обоим источникам; +- [ ] вы различаете отсутствие события и класс строки в готовой сверке. diff --git a/docs/labs/optional/order-amount-difference.md b/docs/labs/optional/order-amount-difference.md new file mode 100644 index 0000000..7a4973b --- /dev/null +++ b/docs/labs/optional/order-amount-difference.md @@ -0,0 +1,70 @@ +# Какая позиция объясняет разницу в сумме + +**Чему учит:** перейти от расхождения суммы заказа к конкретному товару и +проверить объяснение с точностью до копейки. + +Факультатив доступен после ступени 1. В DBeaver используйте пользователя +`analyst` из [инструкции подключения](../../../README.md#подключиться-из-dbeaver). +Дождитесь завершения `etl_pipeline`; оставьте `world_live` на паузе и +дождитесь уже начатых прогонов. + +## Найдите заказ + +В `dm.purchase_vs_orders_v` выберите один заказ с +`mismatch_class = 'amount_delta'`. Возьмите закрытый день: `order_day` +не позднее `max(snapshot_date) - 6` из `ods.order_v`. Сохраните `order_id`, +`order_day`, `declared_revenue` и `items_total`. + +Найдите этот заказ в `dds.order_v`. Сопоставьте `items_total`, `discount`, +`delivery` и `total`. Как связаны эти суммы? Объясните, почему клиентскую +`declared_revenue` нужно сравнивать с `items_total`, чтобы искать разницу +именно в составе корзины. + +## Разберите позиции с двух сторон + +Найдите событие покупки в `ods.event_v`: `purchaseID[1]` совпадает с номером +заказа, `EventDate` — с выбранным днем, `EventType = 'purchase'`. +Откройте поле `ecommerce`. Внутри JSON массив `purchase.products` содержит +позиции клиента с полями `id`, `price` и `quantity`. + +Разверните этот массив в строки и получите артикул, точную цену, количество +и стоимость каждой позиции. Для разбора пригодятся `JSONExtractArrayRaw` +и `JSONExtract`, для разворачивания массива — `ARRAY JOIN`. +Деньги извлекайте как `Decimal` с двумя знаками после запятой. + +С другой стороны используйте массивы `item_sku`, `item_price` и +`item_quantity` заказа в `dds.order_v`. Элементы с одним индексом относятся +к одной позиции; при разворачивании сохраняйте эту связь. + +Сопоставьте позиции по артикулу так, чтобы остались видны товары, +присутствующие только с одной стороны. Покажите, какие позиции совпали, +а какая объясняет разницу. В результате нужны строки товаров, а не только +две итоговые суммы. + +## Проверьте объяснение + +Подтвердите три равенства: + +- сумма стоимостей клиентских позиций равна `declared_revenue`; +- сумма стоимостей позиций магазина равна `items_total`; +- стоимость отсутствующей у магазина позиции равна + `declared_revenue - items_total`. + +Затем сравните точные цены из JSON с готовым массивом `productPrice` +события. По [формату выгрузки](../../formats/clickstream-event.md) цены в нем +округлены до целых рублей. Покажите на своей корзине, что потеряется при +расчете по этому массиву. Если все цены выбранной корзины целые, возьмите +еще один заказ с расхождением и дробной ценой. + +В коротком объяснении назовите заказ, товар, количество, цену и разницу. +Отделите то, что подтверждают данные, от причины, которую по ним установить +нельзя: отсутствие позиции в заказе видно, а причину ее удаления источник +отдельным полем не сообщает. + +## Что должно получиться + +- [ ] позиции клиента и магазина сопоставлены по артикулам; +- [ ] все три равенства сходятся до копейки; +- [ ] найден пример потери точности в готовом массиве цен; +- [ ] расхождение объяснено конкретной позицией, а скидка и доставка + отделены от сравнения корзин.