From c6a20f3cf72f0a4a7fe2e776d125ac05298368a0 Mon Sep 17 00:00:00 2001 From: Dmitry Dementev Date: Sat, 21 Feb 2026 15:13:38 +0300 Subject: [PATCH] =?UTF-8?q?fix(modeling):=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=BF=D0=BE=20=D1=80=D0=B5=D0=B7=D1=83=D0=BB=D1=8C?= =?UTF-8?q?=D1=82=D0=B0=D1=82=D0=B0=D0=BC=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BA?= =?UTF-8?q?=D1=80=D1=91=D1=81=D1=82=D0=BD=D0=BE=D0=B3=D0=BE=20=D1=80=D0=B5?= =?UTF-8?q?=D0=B2=D1=8C=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Зачем: - в статье и скриптах обнаружены фактические ошибки и несостыковки - Что: - README: f.order_date -> d.date_actual в примере витрины (колонки order_date нет в fact_sales) - README: «календарь на 10 лет» -> «5 лет» (соответствует генерации 2023-2027 в скрипте) - 09_solution: bounds CTE теперь использует CURRENT_DATE для открытых интервалов (valid_to IS NULL) - 09_solution: комментарий про дубли в STG при повторном запуске блока 3 - 05_ddl_dm: выравнивание total_line_items - AGENTS.md: обновлён диапазон скриптов 01-09 - Проверка: - визуальная проверка diff Co-Authored-By: Claude Opus 4.6 --- AGENTS.md | 2 +- dwh-modeling/README.md | 6 +++--- dwh-modeling/sql/05_ddl_dm.sql | 2 +- dwh-modeling/sql/09_dml_hw_customer_status_solution.sql | 8 +++++++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ec1f513..6ce2411 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ ## Project Structure & Module Organization - Root `README.md` describes the learning roadmap (RU). -- `dwh-modeling/` contains the article and demo DWH model; SQL lives in `dwh-modeling/sql` as ordered scripts `01_...sql`–`06_...sql`. +- `dwh-modeling/` contains the article and demo DWH model; SQL lives in `dwh-modeling/sql` as ordered scripts `01_...sql`–`09_...sql` (07–09 are homework DDL, template and solution). - `postgres-bookings/` is a Dockerized PostgreSQL + demo “bookings” DB; start it first, then apply DWH scripts against the `demo` database. ## Build, Test, and Development Commands diff --git a/dwh-modeling/README.md b/dwh-modeling/README.md index fa80267..a4d038e 100644 --- a/dwh-modeling/README.md +++ b/dwh-modeling/README.md @@ -180,7 +180,7 @@ flowchart TD |---------|------------| | `dds.dim_customer` | Измерение «Клиент» с историей (SCD Type 2) | | `dds.dim_product` | Измерение «Товар» | -| `dds.dim_date` | Готовый календарь на 10 лет вперёд (день/неделя/месяц/квартал) | +| `dds.dim_date` | Готовый календарь на 5 лет вперёд (день/неделя/месяц/квартал) | | `dds.fact_sales` | Факт «Продажа» — строка заказа с суммой и количеством | 💡 **Суррогатный ключ (Surrogate Key, SK)** — это `BIGINT`, который мы генерируем сами (например, `customer_sk = 1001`). @@ -558,8 +558,8 @@ JOIN dds.dim_product p ON f.product_sk = p.product_sk JOIN dds.dim_customer c ON f.customer_sk = c.customer_sk - AND f.order_date >= c.valid_from - AND (c.valid_to IS NULL OR f.order_date < c.valid_to) -- SCD! + AND d.date_actual >= c.valid_from + AND (c.valid_to IS NULL OR d.date_actual < c.valid_to) -- SCD! GROUP BY d.date_actual, p.product_name, c.customer_segment; ``` diff --git a/dwh-modeling/sql/05_ddl_dm.sql b/dwh-modeling/sql/05_ddl_dm.sql index 214bb46..4b02c46 100644 --- a/dwh-modeling/sql/05_ddl_dm.sql +++ b/dwh-modeling/sql/05_ddl_dm.sql @@ -21,7 +21,7 @@ CREATE TABLE dm.mart_customer_360 ( customer_bk INT NOT NULL, first_order_date DATE, last_order_date DATE, - total_line_items INT NOT NULL, + total_line_items INT NOT NULL, total_items INT NOT NULL, lifetime_value NUMERIC(18,2) NOT NULL, last_email VARCHAR(100), diff --git a/dwh-modeling/sql/09_dml_hw_customer_status_solution.sql b/dwh-modeling/sql/09_dml_hw_customer_status_solution.sql index cf22a73..42686d8 100644 --- a/dwh-modeling/sql/09_dml_hw_customer_status_solution.sql +++ b/dwh-modeling/sql/09_dml_hw_customer_status_solution.sql @@ -147,6 +147,10 @@ SELECT * FROM dds.dim_customer_status ORDER BY customer_bk, valid_from; -- - клиент 104: новый клиент, статус new -- 3.0) Новые события в STG +-- При повторном запуске эти строки добавятся в STG ещё раз (дубли). +-- Для демо это не страшно: ODS-вставка ниже использует ON CONFLICT DO NOTHING, +-- а SCD2-блок защищён от повторных вставок через NOT EXISTS. +-- В продакшене STG обычно очищается перед каждой загрузкой (TRUNCATE / партиция по дате). INSERT INTO stg.customer_status_raw (customer_id, status, event_ts, _load_id, _load_ts) VALUES ('101','active','2024-11-15 09:00:00','batch_20241115_1000','2024-11-15 10:00:00'), ('102','active','2024-05-05 09:30:00','batch_20240505_1000','2024-05-05 10:00:00'), @@ -305,7 +309,9 @@ TRUNCATE dm.mart_customer_status_daily; WITH bounds AS ( SELECT min(valid_from) AS date_from, - max(coalesce(valid_to, valid_from)) AS date_to + -- CURRENT_DATE для открытых интервалов (valid_to IS NULL = текущий статус), + -- иначе витрина не покроет даты после последней смены статуса. + max(coalesce(valid_to, CURRENT_DATE)) AS date_to FROM dds.dim_customer_status ) INSERT INTO dm.mart_customer_status_daily (