From a234c2954d8044919eeaf1f7370062431ab1e8ab Mon Sep 17 00:00:00 2001 From: Dmitry Dementev Date: Sat, 8 Nov 2025 20:40:04 +0300 Subject: [PATCH] =?UTF-8?q?=D0=92=D1=8B=D1=87=D0=B8=D1=82=D0=BA=D0=B0=20?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D1=82=D1=8C=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dwh-modeling/README.md | 200 +++++++++++++++++++++++++++++++++-------- 1 file changed, 163 insertions(+), 37 deletions(-) diff --git a/dwh-modeling/README.md b/dwh-modeling/README.md index e80daba..69e0293 100644 --- a/dwh-modeling/README.md +++ b/dwh-modeling/README.md @@ -296,39 +296,177 @@ fact_sales.order_date BETWEEN dim_customer.valid_from AND dim_customer.valid_to --- -### **3. Data Vault 2.0** -*Источник: Дэн Линстедт (Dan Linstedt)* +### **3. Data Vault 2.0 — «конструктор Lego» для больших DWH** -Модель основана на трёх типах таблиц: +*Идея: Дэн Линстедт (Dan Linstedt). Цель — собирать DWH из повторно используемых блоков, не боясь, что новый источник «сломает» всё, что было до него.* -| Тип | Назначение | Пример | -|-----|------------|--------| -| **Hub (Хаб)** | Хранит бизнес-ключи (BK) | `hub_customer`: `customer_id`, `load_dttm` | -| **Link (Связь)** | Фиксирует отношения | `link_order_customer`: `order_id + customer_id` | -| **Satellite (Сателлит)** | Хранит атрибуты + историю | `sat_customer_info`: имя, email, дата начала/окончания | +#### С чего начать? Представьте конструктор + +У вас есть коробка Lego. В ней: +- **Красные кирпичи (Хабы)** — это *«что или кто»*: клиент, заказ, товар. + У них нет цвета, надписей — только форма (бизнес-ключ). Главное — *узнать*, что это «один и тот же клиент №101», даже если его email менялся трижды. +- **Серые соединители (Линки)** — это *«как связаны»*: «заказ №5001 создан клиентом №101». + Не сами объекты, а *связь между ними*. И даже эту связь можно «отвязать» и привязать по-другому — без пересборки кирпичей. +- **Жёлтые наклейки (Сателлиты)** — это *«какие у них свойства»*: имя клиента, статус заказа, цена товара. + И самое важное: **каждый раз, когда что-то изменилось — наклеиваем новую жёлтую наклейку**, не стирая старую. Так у нас остаётся *полная история*. + +💡 **Главный принцип DV**: +> *«Идентичность — навсегда. Свойства — меняются. Связи — тоже могут меняться. И всё это — отдельно.»* ```mermaid erDiagram - hub_customer ||--o{ sat_customer_info : "хаб → сателлит" - hub_order ||--o{ sat_order_details : "хаб → сателлит" - hub_customer ||--o{ link_order_customer : "участвует в" - hub_order ||--o{ link_order_customer : "создан клиентом" + %% Хабы - красные кирпичи (идентичность) + %% Связи между компонентами + HUB_CUSTOMER ||--o{ SAT_CUSTOMER_INFO : "имеет" + HUB_CUSTOMER ||--o{ LINK_ORDER_CUSTOMER : "участвует_в" + HUB_ORDER ||--o{ LINK_ORDER_CUSTOMER : "создан" + LINK_ORDER_CUSTOMER ||--o{ SAT_ORDER_STATUS : "имеет_статус" + + HUB_CUSTOMER { + varchar customer_bk PK "Бизнес-ключ: '101'" + varchar record_source + timestamp load_dttm + } + + HUB_ORDER { + varchar order_id PK "Бизнес-ключ: '5001'" + varchar record_source + timestamp load_dttm + } + + %% Линки - серые соединители (связи) + LINK_ORDER_CUSTOMER { + varchar link_key PK "Хэш от (order_id+customer_bk)" + varchar order_id FK "→ HUB_ORDER" + varchar customer_bk FK "→ HUB_CUSTOMER" + varchar record_source + timestamp load_dttm + } + + %% Сателлиты - жёлтые наклейки (атрибуты + история) + SAT_CUSTOMER_INFO { + varchar customer_bk FK "→ HUB_CUSTOMER" + varchar hashdiff + timestamp valid_from + timestamp valid_to + varchar name + varchar email + varchar city + varchar record_source + timestamp load_dttm + } + + SAT_ORDER_STATUS { + varchar link_key FK "→ LINK_ORDER_CUSTOMER" + varchar hashdiff + timestamp valid_from + timestamp valid_to + varchar status + varchar record_source + timestamp load_dttm + } + ``` -✅ **Плюсы**: -- **История «из коробки»** — каждое изменение — новая строка в сателлите; -- **Масштабируемость**: легко подключить новый источник — добавили хаб/линк/сателлит; -- **Аудит**: откуда пришёл каждый факт — видно по `_load_dttm`. +> **Как читать схему:** +> 🔴 **Красные блоки (Хабы)** = «Кто/что это?» — только идентичность, никаких атрибутов. +> ⚪ **Серые блоки (Линки)** = «Как связаны?» — соединяют хабы, фиксируют отношения. +> 🟡 **Жёлтые блоки (Сателлиты)** = «Какие свойства?» — хранят атрибуты + историю изменений. +> +> 💡 **Правило конструктора:** Чтобы добавить новый источник (например, мобильное приложение), мы просто «приклеиваем» новый сателлит к существующему хабу — не перестраивая всю модель. -❌ **Минусы**: -- Сложно читать (20 таблиц вместо 3); -- Нужно писать сложные запросы (или использовать автоматическую генерацию витрин). +--- -📌 **Когда выбирать**: -→ Большие проекты с 10+ источниками; -→ Когда критична **трассировка** и **соответствие регуляторным требованиям** (например, финтех). +#### Почему это не просто SCD Type 2 «по-другому»? -> 📌 *DV 1.0* не поддерживал SCD Type 2 «из коробки» — в DV 2.0 это решено. +В SCD Type 2 (в `dim_customer`) мы *смешиваем*: +- идентичность (`customer_bk = 101`), +- и атрибуты (`email`, `city`), +- и связь с бизнес-событиями (`valid_from`, `is_current`). + +В Data Vault эти три вещи **разнесены по разным таблицам**: +- `hub_customer` — только `customer_bk` и технические поля (откуда пришёл, когда загрузили); +- `sat_customer_info` — только атрибуты + их история (как в SCD, но отдельно!); +- `link_order_customer` — только «кто сделал заказ» (и даже тут — отдельный сателлит может хранить *статус связи*: «активный», «отменённый» и т.д.). + +➡️ Это даёт **огромный бонус**: +Если завтра пришёл новый источник — например, мобильное приложение — и там у клиента есть `device_id`, +вы **не перестраиваете `dim_customer`** (как в Kimball/3NF), +а просто добавляете **новый сателлит** — `sat_customer_device`. +Старые отчёты продолжают работать. Новые — используют новую наклейку. + +--- + +#### Как устроен «сырой» Data Vault (упрощённо) + +| Таблица | Внутри | Пример | +|---------|--------|--------| +| **`hub_customer`** | `customer_bk` (например, `'101'`) + `load_dttm` + `source` | Клиент «101» появился в CRM 10 января | +| **`sat_customer_info`** | `customer_bk`, `email`, `city`, `valid_from`, `valid_to`, `load_dttm` | 10 янв–15 мая: `a@ex.com`, Москва; 16 мая–…: `b@ex.com`, Москва | +| **`hub_order`** | `order_id` + `load_dttm` + `source` | Заказ «5001» из системы заказов | +| **`link_order_customer`** | `order_id`, `customer_bk` + `load_dttm` + `source` | Заказ 5001 → клиент 101 | +| **`sat_order_status`** *(опционально)* | `order_id`, `status`, `valid_from`, `valid_to` | 10 янв: «оплачен», 12 янв: «доставлен» | + +> ⚠️ *На практике вместо `customer_bk` часто используют **хэш-ключ** (`hk_customer`) — чтобы сравнивать быстрее и избежать проблем с типами (число vs строка). + +--- + +#### А как же отчёты? Где витрины? + +Хороший вопрос. **Сырой DV — не для BI.** Это «склад запчастей». +Чтобы собрать «машину», нужен второй шаг: **Business Vault** (в DV 2.0 это — обязательная часть!). + +Там уже: +- **PIT-таблицы (Point-in-Time)** — «какой клиент был 12 января 2024?» за 1 JOIN, а не за 5 оконных функций; +- **Bridge-таблицы** — готовые пути: «заказ → клиент → последняя версия профиля»; +- **Derived-таблицы** — после бизнес-логики (скидки, сегментация, флаги). + +А *уже из них* строят **витрины в формате Звезды** — те самые `mart_daily_sales`, которые вы подключаете в Power BI. + +🔁 То есть цепочка: +**Источник → Hub/Link/Sat (Raw DV) → PIT/Bridge (Business Vault) → Star Schema (DM)** +Можно строить и напрямую, без PIT/Bridge таблиц. Но это значительно "тяжелее" с точки зрения затрат вычислительных ресурсов. + +--- + +#### Плюсы и минусы — честно и без прикрас + +| ✅ Плюсы | ❌ Минусы | +|---------|----------| +| **История «из коробки»** — каждое изменение видно, ничего не перезаписывается | Сырой слой тяжел для чтения — нужен Business Vault (доп. работа) | +| **Добавить источник — легко** (новый сателлит, не ломая старое) | **Новых таблиц много** (Hub/Link/Sat — уже 3 на простую сущность) | +| **Полная трассировка**: кто, откуда, когда привёз каждую строчку | **Требует дисциплины**: если `record_source` забыли — аудит сломан | +| **Параллельная работа**: одна команда — клиенты, другая — заказы | **Не для MVP**: окупается на 10+ источниках, не на двух CSV | + +--- + +#### Когда Data Vault — ваш выбор? + +| Выберите DV 2.0, если… | Не начинайте с DV, если… | +|------------------------|---------------------------| +| У вас 5+ разнородных систем (CRM, ERP, мобильные приложения, внешние API) | У вас 1–2 источника и нужно быстро дать отчёт | +| Схемы часто меняются (новые атрибуты, новые связи) | Схема стабильна (например, учёт договоров в банке) | +| Важен аудит: «кто и когда изменил email?» (финтех, госсектор) | Отчёты — главная цель, а не соответствие регуляторам | +| В будущем — масштабирование: новые домены, новые команды | Вы один или вдвоём, и хотите простоты | + +> 🎯 **Совет для новичка**: +> Пока учитесь — *не пытайтесь собрать DV вручную*. Но **понимать его логику — обязательно**. +> Почему? Потому что многие современные enterprise-DWH сегодня — либо DV, либо гибриды (DV + Star). +> Даже если вы будете работать с витринами — вы будете *понимать*, откуда берутся данные и почему история «не обновляется». + +--- + +#### Важно: DV 1.0 vs DV 2.0 — в чём разница? + +- **DV 1.0** — только Hub/Link/Sat + историзация через `load_dttm`. + *Проблема:* нет стандартного способа хранить *периоды действия* (как в SCD Type 2) — `valid_from/valid_to` приходилось изобретать самим. + +- **DV 2.0** — стандартизирует: + - **Effectivity Satellites** (с `valid_from/valid_to`) — для явной историчности, + - **Multi-Active Satellites** — когда у объекта *несколько одновременных значений* (например, три активных телефона), + - **Business Vault** — как обязательный слой перед витринами. + +➡️ Сегодня, когда говорят «Data Vault», почти всегда имеют в виду **DV 2.0**. --- @@ -377,7 +515,7 @@ quadrantChart ## **7. Практикум: как собрать первую витрину** -Покажем на примере `mart_daily_sales` — таблицу, которую можно сразу подключить к Power BI. +Покажем на примере `mart_daily_sales` — таблицу, которую можно сразу подключить к BI. ### **Этапы сборки** @@ -454,7 +592,7 @@ GROUP BY d.date_actual, p.product_name, c.customer_segment; 1. **Начните с витрины в формате Звезды (Star Schema).** — Это просто: одна таблица фактов + несколько «плоских» справочников. - — Это быстро: отчёт в Power BI — за 10 минут. + — Это быстро: отчёт в BI — за 10 минут. — Это понятно: даже менеджер поймёт структуру. 2. **Стройте DDS только когда это *действительно нужно*.** @@ -607,18 +745,6 @@ SELECT 'OK' WHERE EXISTS ( --- -### 📖 Что почитать дальше? - -| Книга / Ресурс | Для кого | -|----------------|----------| -| **Кимболл, «Техника создания хранилищ данных»** | Начинающим: звезда, SCD, витрины | -| **Инмон, «Построение хранилищ данных»** | Для понимания 3NF и корпоративного подхода | -| **Linstedt & Olschimke, «Data Vault 2.0»** | Практическое руководство по DV | -| **[anchor-modeling.com](https://www.anchormodeling.com/)** | Официальный сайт Anchor Modeling | -| **Курс «Аналитика в Postgres» (Stepik / Postgres Pro)** | Практика SQL + DWH на реальных данных | - ---- - ## **ЧАСТЬ C. Мини-датасет (для практики)** Положите эти файлы в папку `data/` — и тренируйтесь: