diff --git a/dwh-modeling/Homework_Customer_Status_DDS_DM.md b/dwh-modeling/Homework_Customer_Status_DDS_DM.md index 15926e3..dff1dfc 100644 --- a/dwh-modeling/Homework_Customer_Status_DDS_DM.md +++ b/dwh-modeling/Homework_Customer_Status_DDS_DM.md @@ -76,11 +76,44 @@ customer_id,status,event_ts,_load_id,load_ts ### 3.1. STG: загрузка CSV -1. Откройте `psql` в контейнере (см. инструкцию в `postgres-bookings/README.md`). -2. Загрузите файл `customer_status_events.csv` в таблицу `stg.customer_status_raw`: - - можно использовать `\copy` из `psql`; - - либо любой другой способ, к которому вы привыкли. -3. Убедитесь, что данные загрузились: +Есть два варианта — выберите любой. Для первого прохождения рекомендуем **вариант A** (самый простой). + +#### Вариант A (рекомендуемый): вставить данные в STG через `INSERT` + +Откройте SQL‑клиент (DBeaver или `psql`) и вставьте данные текстом: + +> 💡 Если вы уже загружали данные в `stg.customer_status_raw` и делаете повторный запуск — начните с `TRUNCATE stg.customer_status_raw;`. +> +> 💡 В примере ниже показан минимальный набор для клиента 101. Чтобы получить несколько клиентов и больше событий — используйте вариант B (CSV) или добавьте строки из файла `dwh-modeling/data/customer_status_events.csv`. + +```sql +INSERT INTO stg.customer_status_raw (customer_id, status, event_ts, _load_id, _load_ts) VALUES +('101','new','2024-01-01 09:00:00','batch_20240101_1000','2024-01-01 10:00:00'), +('101','active','2024-02-15 10:30:00','batch_20240215_1100','2024-02-15 11:00:00'), +('101','vip','2024-05-10 11:00:00','batch_20240510_1200','2024-05-10 12:00:00'), +('101','churned','2024-09-01 12:15:00','batch_20240901_1300','2024-09-01 13:00:00'); +``` + +> 💡 Здесь `_load_ts` — это время загрузки (в CSV оно называется `load_ts`). + +#### Вариант B: загрузить CSV + +Можно загрузить файл `dwh-modeling/data/customer_status_events.csv` в таблицу `stg.customer_status_raw`: + +- **Через DBeaver**: Import Data → CSV → `stg.customer_status_raw` (колонку `load_ts` маппить в `_load_ts`). +- **Через `psql` в контейнере (`./psql_sh`)**: без установки `psql` на хост. + +Способ: передайте CSV в `psql` через STDIN и выполните `\copy ... FROM STDIN`: + +```bash +./postgres-bookings/psql_sh -c "TRUNCATE stg.customer_status_raw;" +cat dwh-modeling/data/customer_status_events.csv | ./postgres-bookings/psql_sh -c \ + "\\copy stg.customer_status_raw(customer_id,status,event_ts,_load_id,_load_ts) FROM STDIN WITH (FORMAT csv, HEADER true)" +``` + +> 💡 Для части 5 (инкрементальная загрузка) `TRUNCATE stg.customer_status_raw` делать не нужно — загружайте только новые строки. + +После загрузки убедитесь, что данные на месте: ```sql SELECT * FROM stg.customer_status_raw LIMIT 10; @@ -97,6 +130,8 @@ SELECT * FROM stg.customer_status_raw LIMIT 10; - аккуратно обработать возможные пустые значения (если бы они были); - заполнить `_load_id` и `_load_ts` в `ods.customer_status`. +> 💡 Для простоты сделайте загрузку идемпотентной через `TRUNCATE ods.customer_status;` перед `INSERT` (таблица в ODS имеет первичный ключ `(customer_id, event_ts)`). + Проверьте, что в `ods.customer_status` данные выглядят аккуратно: ```sql @@ -135,8 +170,15 @@ ORDER BY customer_id, event_ts; - `valid_from` — `event_ts::date`, - `valid_to` — следующий `event_ts::date` (а у последней версии `valid_to = NULL`). +> 💡 Упрощение для домашки: считаем, что у клиента не бывает двух разных смен статуса в один и тот же день. +> Если такое бывает — удобнее строить периоды в `TIMESTAMP` (или вводить дополнительное правило сортировки), но это уже усложнение. + +> 💡 Если в событиях встречаются повторы одного и того же статуса подряд, можно отфильтровать “не-изменения” через `LAG(status)` (или `LAG(hashdiff)`) перед расчётом `LEAD()`. + 3. Вставьте получившиеся строки в `dds.dim_customer_status`. Актуальная строка для клиента — та, где `valid_to IS NULL`. +> 💡 Для первой версии решения можно сделать full refresh: перед вставкой очистить таблицу (`TRUNCATE dds.dim_customer_status;`), как в основном примере с `dds.dim_customer`. + ```sql INSERT INTO dds.dim_customer_status ( customer_bk, status, hashdiff, @@ -192,6 +234,11 @@ ORDER BY customer_bk, valid_from; Опциональное задание для закрепления: собрать небольшую витрину с количеством клиентов по статусам на каждую дату. +Перед началом убедитесь, что слой DM создан (схема `dm` и таблицы): + +- выполните `dwh-modeling/sql/05_ddl_dm.sql` (один раз); +- затем можно собирать витрину. + Пример целевой таблицы: ```sql diff --git a/postgres-bookings/psql_sh b/postgres-bookings/psql_sh index 2692371..b6e4e74 100755 --- a/postgres-bookings/psql_sh +++ b/postgres-bookings/psql_sh @@ -1,2 +1,20 @@ #!/bin/bash -docker compose exec -it db psql -U ${POSTGRES_USER:-postgres} -d demo +set -euo pipefail + +# Usage: +# ./psql_sh # interactive psql inside container +# ./psql_sh -c "SELECT 1;" # run a command +# cat file.csv | ./psql_sh -c "\\copy ... FROM STDIN WITH (FORMAT csv, HEADER true)" + +script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +compose_file="${script_dir}/docker-compose.yml" + +compose() { + docker compose -f "${compose_file}" --project-directory "${script_dir}" "$@" +} + +if [ -t 0 ]; then + compose exec -it db psql -U "${POSTGRES_USER:-postgres}" -d demo "$@" +else + compose exec -T db psql -U "${POSTGRES_USER:-postgres}" -d demo "$@" +fi