Улучшения домашки
This commit is contained in:
@@ -76,11 +76,44 @@ customer_id,status,event_ts,_load_id,load_ts
|
|||||||
|
|
||||||
### 3.1. STG: загрузка CSV
|
### 3.1. STG: загрузка CSV
|
||||||
|
|
||||||
1. Откройте `psql` в контейнере (см. инструкцию в `postgres-bookings/README.md`).
|
Есть два варианта — выберите любой. Для первого прохождения рекомендуем **вариант A** (самый простой).
|
||||||
2. Загрузите файл `customer_status_events.csv` в таблицу `stg.customer_status_raw`:
|
|
||||||
- можно использовать `\copy` из `psql`;
|
#### Вариант A (рекомендуемый): вставить данные в STG через `INSERT`
|
||||||
- либо любой другой способ, к которому вы привыкли.
|
|
||||||
3. Убедитесь, что данные загрузились:
|
Откройте 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
|
```sql
|
||||||
SELECT * FROM stg.customer_status_raw LIMIT 10;
|
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`.
|
- заполнить `_load_id` и `_load_ts` в `ods.customer_status`.
|
||||||
|
|
||||||
|
> 💡 Для простоты сделайте загрузку идемпотентной через `TRUNCATE ods.customer_status;` перед `INSERT` (таблица в ODS имеет первичный ключ `(customer_id, event_ts)`).
|
||||||
|
|
||||||
Проверьте, что в `ods.customer_status` данные выглядят аккуратно:
|
Проверьте, что в `ods.customer_status` данные выглядят аккуратно:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
@@ -135,8 +170,15 @@ ORDER BY customer_id, event_ts;
|
|||||||
- `valid_from` — `event_ts::date`,
|
- `valid_from` — `event_ts::date`,
|
||||||
- `valid_to` — следующий `event_ts::date` (а у последней версии `valid_to = NULL`).
|
- `valid_to` — следующий `event_ts::date` (а у последней версии `valid_to = NULL`).
|
||||||
|
|
||||||
|
> 💡 Упрощение для домашки: считаем, что у клиента не бывает двух разных смен статуса в один и тот же день.
|
||||||
|
> Если такое бывает — удобнее строить периоды в `TIMESTAMP` (или вводить дополнительное правило сортировки), но это уже усложнение.
|
||||||
|
|
||||||
|
> 💡 Если в событиях встречаются повторы одного и того же статуса подряд, можно отфильтровать “не-изменения” через `LAG(status)` (или `LAG(hashdiff)`) перед расчётом `LEAD()`.
|
||||||
|
|
||||||
3. Вставьте получившиеся строки в `dds.dim_customer_status`. Актуальная строка для клиента — та, где `valid_to IS NULL`.
|
3. Вставьте получившиеся строки в `dds.dim_customer_status`. Актуальная строка для клиента — та, где `valid_to IS NULL`.
|
||||||
|
|
||||||
|
> 💡 Для первой версии решения можно сделать full refresh: перед вставкой очистить таблицу (`TRUNCATE dds.dim_customer_status;`), как в основном примере с `dds.dim_customer`.
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
INSERT INTO dds.dim_customer_status (
|
INSERT INTO dds.dim_customer_status (
|
||||||
customer_bk, status, hashdiff,
|
customer_bk, status, hashdiff,
|
||||||
@@ -192,6 +234,11 @@ ORDER BY customer_bk, valid_from;
|
|||||||
|
|
||||||
Опциональное задание для закрепления: собрать небольшую витрину с количеством клиентов по статусам на каждую дату.
|
Опциональное задание для закрепления: собрать небольшую витрину с количеством клиентов по статусам на каждую дату.
|
||||||
|
|
||||||
|
Перед началом убедитесь, что слой DM создан (схема `dm` и таблицы):
|
||||||
|
|
||||||
|
- выполните `dwh-modeling/sql/05_ddl_dm.sql` (один раз);
|
||||||
|
- затем можно собирать витрину.
|
||||||
|
|
||||||
Пример целевой таблицы:
|
Пример целевой таблицы:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
|
|||||||
@@ -1,2 +1,20 @@
|
|||||||
#!/bin/bash
|
#!/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
|
||||||
|
|||||||
Reference in New Issue
Block a user