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

12 KiB
Raw Blame History

Домашка: статусы клиента от 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

Структура файла:

customer_id,status,event_ts,_load_id,load_ts
101,new,2024-01-01 09:00:00,batch_20240101_1000,2024-01-01 10: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. Убедитесь, что данные загрузились:
SELECT * FROM stg.customer_status_raw LIMIT 10;

3.2. ODS: очистка и типизация

В файле 08_dml_hw_customer_status_template.sql найдите заготовку блока ODS и допишите SQL:

  • привести:
    • customer_idINT,
    • statusVARCHAR(20) (можно оставить как есть),
    • event_ts и load_tsTIMESTAMP (в DWH-таблицах эта колонка будет лежать как _load_ts);
  • аккуратно обработать возможные пустые значения (если бы они были);
  • заполнить _load_id и _load_ts в ods.customer_status.

Проверьте, что в ods.customer_status данные выглядят аккуратно:

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 — период, когда статус был актуален;
  • 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() посчитайте (в учебном варианте считаем, что обновление DWH идёт раз в день, поэтому используем DATE):

    • valid_fromevent_ts::date,
    • valid_to — следующий event_ts::date (а у последней версии valid_to = NULL).
  3. Вставьте получившиеся строки в dds.dim_customer_status. Актуальная строка для клиента — та, где valid_to IS NULL.

INSERT INTO dds.dim_customer_status (
    customer_bk, status, hashdiff,
    valid_from, valid_to,
    created_at, updated_at
)
SELECT
    ...

Проверьте результат:

SELECT *
FROM dds.dim_customer_status
ORDER BY customer_bk, valid_from;

Ожидаемое поведение:

  • у клиента 101 несколько строк с разными статусами и непересекающимися периодами;
  • valid_to IS NULL только у самой свежей строки для каждого клиента.

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 датой начала новой версии);
    • вставить новую строку с valid_to = NULL.

Эта часть особенно полезна, если вы хотите почувствовать, как SCD2 живёт в реальном DWH.


6. Часть 4 — витрина в DM (по желанию)

Опциональное задание для закрепления: собрать небольшую витрину с количеством клиентов по статусам на каждую дату.

Пример целевой таблицы:

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.

Пример запроса к витрине:

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.