Files
de-roadmap/dwh-modeling/Homework_Customer_Status_DDS_DM.md
T

241 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Домашка: статусы клиента от STG до DDS (и немного DM)
Небольшое практическое задание на 1–2 вечера: по данным о смене статусов клиента (CRM) построить цепочку слоёв `STG → ODS → DDS (SCD2)` и, по желанию, небольшую витрину в `dm`.
Цель — потренировать **руками**:
- работу со слоями DWH (stg / ods / dds / dm);
- проектирование и загрузку **измерения с историей (SCD Type 2)**;
- аккуратную работу со временем (`event_ts`, `valid_from`, `valid_to`).
Исходим из того, что вы уже прошли основную статью `dwh-modeling/README.md` и познакомились с примером интернет‑магазина.
---
## 1. Данные: события смены статуса клиента
Представьте, что в CRM для каждого клиента хранится история статусов:
- `new` — только что зарегистрировался;
- `active` — делал покупки недавно;
- `vip` — часто покупает и много тратит;
- `churned` — давно ничего не делал, считаем «отвалившимся».
Эта информация приходит в DWH в виде **событий** (events): «у клиента X в момент времени Y статус стал Z».
В репозитории в каталоге `dwh-modeling/data` лежит файл:
- `customer_status_events.csv`
Структура файла:
```text
customer_id,status,event_ts,_load_id,load_ts
101,new,2024-01-01 09:00:00,batch_20250405_0800,2025-04-05 08:00:00
...
```
Колонки:
- `customer_id` — бизнес-ключ клиента (тот же, что и в основном примере — 101, 102, 103);
- `status` — статус клиента в CRM (`new`, `active`, `vip`, `churned`);
- `event_ts` — момент, когда статус сменился в CRM;
- `_load_id` — идентификатор батча загрузки;
- `load_ts` — момент, когда данные попали в DWH (в таблицах STG/ODS эта колонка будет называться `_load_ts`, но по смыслу это то же самое время загрузки).
Файл содержит несколько клиентов и несколько смен статуса по каждому — этого достаточно, чтобы отработать SCD2.
---
## 2. Целевая схема: какие таблицы уже есть
Чтобы не тратить время на DDL, структуры таблиц для домашки уже подготовлены в `dwh-modeling/sql`:
- `07_ddl_hw_customer_status.sql` — создаёт дополнительные таблицы:
- `stg.customer_status_raw` — сырые события о статусе клиента;
- `ods.customer_status` — очищенные и типизированные события;
- `dds.dim_customer_status` — измерение статусов клиента в формате **SCD Type 2**.
- `08_dml_hw_customer_status_template.sql` — шаблон DML-скрипта с подсказками и заготовками блоков.
Перед началом работы:
1. Поднимите demo‑Postgres по инструкции из корневого `README.md`.
2. Выполните базовые скрипты DWH:
- `01_ddl_stg-dds.sql`
- `02_dml_stg-dds.sql`
3. Выполните DDL для домашки:
- `07_ddl_hw_customer_status.sql`
После этого схемы `stg`, `ods`, `dds` уже существуют, а дополнительные таблицы для статусов созданы.
---
## 3. Часть 1 — STG → ODS (обязательно)
**Задача:** загрузить CSV в STG и переложить данные в ODS с приведением типов.
### 3.1. STG: загрузка CSV
1. Откройте `psql` в контейнере (см. инструкцию в `postgres-bookings/README.md`).
2. Загрузите файл `customer_status_events.csv` в таблицу `stg.customer_status_raw`:
- можно использовать `\copy` из `psql`;
- либо любой другой способ, к которому вы привыкли.
3. Убедитесь, что данные загрузились:
```sql
SELECT * FROM stg.customer_status_raw LIMIT 10;
```
### 3.2. ODS: очистка и типизация
В файле `08_dml_hw_customer_status_template.sql` найдите заготовку блока ODS и допишите SQL:
- привести:
- `customer_id``INT`,
- `status``VARCHAR(20)` (можно оставить как есть),
- `event_ts` и `load_ts``TIMESTAMP` (в DWH-таблицах эта колонка будет лежать как `_load_ts`);
- аккуратно обработать возможные пустые значения (если бы они были);
- заполнить `_load_id` и `_load_ts` в `ods.customer_status`.
Проверьте, что в `ods.customer_status` данные выглядят аккуратно:
```sql
SELECT *
FROM ods.customer_status
ORDER BY customer_id, event_ts;
```
---
## 4. Часть 2 — ODS → DDS (SCD Type 2, обязательно)
**Задача:** по событиям в `ods.customer_status` построить измерение `dds.dim_customer_status`, где каждая строка — период действия статуса.
Целевая таблица уже создана (см. `07_ddl_hw_customer_status.sql`):
- `customer_bk` — бизнес-ключ клиента (тот же, что `customer_id` в ODS);
- `status` — статус клиента;
- `hashdiff` — хэш от атрибутов (здесь достаточно самого `status`);
- `valid_from` / `valid_to` — период, когда статус был актуален;
- `is_current` — флаг актуальной строки;
- `created_at` / `updated_at` — технические поля.
### 4.1. Начальная загрузка SCD2
В шаблоне `08_dml_hw_customer_status_template.sql` допишите блок начальной загрузки:
1. Сформируйте промежуточный набор:
- `customer_bk`,
- `status`,
- `event_ts` (как «время начала действия статуса»),
- `hashdiff` (например, `md5(status)`; можно вынести расчёт в отдельную функцию по аналогии с `dds.customer_hash` для клиентов).
2. Для каждого клиента отсортируйте события по `event_ts` и с помощью `LEAD()` посчитайте:
- `valid_from` — текущее `event_ts`,
- `valid_to` — следующее `event_ts - 1 second` (или `9999-12-31`, если следующего нет).
3. Вставьте получившиеся строки в `dds.dim_customer_status`. У актуальной строки для каждого клиента задайте `is_current = TRUE` (например, там, где `valid_to = '9999-12-31'`), у остальных — `FALSE`:
```sql
INSERT INTO dds.dim_customer_status (
customer_bk, status, hashdiff,
valid_from, valid_to,
is_current, created_at, updated_at
)
SELECT
...
```
Проверьте результат:
```sql
SELECT *
FROM dds.dim_customer_status
ORDER BY customer_bk, valid_from;
```
Ожидаемое поведение:
- у клиента 101 несколько строк с разными статусами и непересекающимися периодами;
- `is_current = TRUE` только у самой свежей строки для каждого клиента.
### 4.2. Проверка себя
Примеры проверочных запросов (можно придумать свои):
- «Какой статус был у клиента 101 на дату `2024-06-01`
→ одна строка с нужным статусом.
- «Сколько клиентов были в статусе `active` на `2024-04-10`
→ несколько строк, если статус *активен* для диапазона дат.
---
## 5. Часть 3 — инкрементальная загрузка (по желанию)
Если хочется потренироваться глубже:
1. Добавьте в CSV ещё несколько событий смены статуса (например, переход части клиентов из `churned` обратно в `active`).
2. Загрузите новые строки только в `stg.customer_status_raw`.
3. Напишите логику инкрементального обновления `dds.dim_customer_status`:
- ориентируйтесь на пример из `03_demo_increment.sql` для `dds.dim_customer`;
- важно:
- корректно «закрыть» старую актуальную строку (`valid_to`, `is_current = FALSE`);
- вставить новую строку с `is_current = TRUE`.
Эта часть особенно полезна, если вы хотите почувствовать, как SCD2 живёт в реальном DWH.
---
## 6. Часть 4 — витрина в DM (по желанию)
Опциональное задание для закрепления: собрать небольшую витрину с количеством клиентов по статусам на каждую дату.
Пример целевой таблицы:
```sql
CREATE TABLE dm.mart_customer_status_daily (
date_actual DATE NOT NULL,
status VARCHAR(20) NOT NULL,
customers_cnt INT NOT NULL
);
```
Идея:
- использовать `dds.dim_date` как календарь;
- для каждой `date_actual` найти, какой статус был у клиента в этот день
(через `JOIN` на `dds.dim_customer_status` по диапазону `valid_from/valid_to`);
- агрегировать по `status`.
Пример запроса к витрине:
```sql
SELECT
date_actual,
status,
customers_cnt
FROM dm.mart_customer_status_daily
WHERE date_actual BETWEEN '2024-04-01' AND '2024-04-30'
ORDER BY date_actual, status;
```
---
## 7. Как вписать эту домашку в обучение
Рекомендуемое место в дорожке:
1. Пройти основную теорию по DWH и SCD:
- `dwh-modeling/README.md`
- `dwh-modeling/SCD.md`
2. Разобрать базовый пример интернет‑магазина (скрипты `01_``06_`).
3. Выполнить **эту домашку** как первую попытку «самостоятельного» моделирования и ETL:
- познакомиться с ещё одним измерением с историей (`dim_customer_status`);
- потренироваться аккуратно работать с датами и периодами;
- при желании — собрать простую витрину в `dm`.
Если что‑то не получается — можно разбирать решения по шагам вместе с ментором: от простого `SELECT` из STG до полноценного SCD2 в DDS.