Переработка структуры
This commit is contained in:
+202
-220
@@ -1,6 +1,16 @@
|
|||||||
# **Data Vault 2.0: как собрать хранилище как конструктор**
|
# **Data Vault 2.0: как собрать хранилище как конструктор**
|
||||||
|
|
||||||
*Для тех, кто уже слышал про STG/ODS/DDS/DM, факты/измерения и SCD, но хочет разобраться, что такое Data Vault и зачем он вообще нужен.*
|
## 📚 Оглавление
|
||||||
|
|
||||||
|
1. [Зачем вообще нужен Data Vault?](#1-зачем-вообще-нужен-data-vault)
|
||||||
|
2. [Интуиция: DV как конструктор Lego](#2-интуиция-dv-как-конструктор-lego)
|
||||||
|
3. [Три типа таблиц в Data Vault 2.0](#3-три-типа-таблиц-в-data-vault-20)
|
||||||
|
4. [Типы сателлитов в DV 2.0](#4-типы-сателлитов-в-dv-20)
|
||||||
|
5. [Raw Vault и Business Vault](#5-raw-vault-и-business-vault)
|
||||||
|
6. [Пример: клиент и заказы в DV-стиле](#6-пример-клиент-и-заказы-в-dv-стиле)
|
||||||
|
7. [Как это живёт в пайплайне загрузки](#7-как-это-живёт-в-пайплайне-загрузки)
|
||||||
|
8. [Плюсы и минусы Data Vault](#8-плюсы-и-минусы-data-vault)
|
||||||
|
9. [Когда DV стоит использовать, а когда нет](#9-когда-dv-стоит-использовать-а-когда-нет)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -41,43 +51,43 @@
|
|||||||
|
|
||||||
* 🔴 **Hub (Хаб)** — *«кто/что это»*
|
* 🔴 **Hub (Хаб)** — *«кто/что это»*
|
||||||
Сущности: клиент, заказ, договор, счёт.
|
Сущности: клиент, заказ, договор, счёт.
|
||||||
Внутри: бизнес-ключ (`customer_id`, `contract_number`) + техполя.
|
Внутри: бизнес‑ключ (например, customer_id или contract_number) и техполя.
|
||||||
|
|
||||||
* ⚪ **Link (Линк)** — *«как они связаны»*
|
* ⚪ **Link (Линк)** — *«как они связаны»*
|
||||||
«Клиент сделал заказ», «договор относится к счёту».
|
«Клиент сделал заказ», «договор относится к счёту».
|
||||||
Внутри: ссылки на хабы + техполя.
|
Внутри: ссылки на хабы и техполя.
|
||||||
|
|
||||||
* 🟡 **Satellite (Сателлит)** — *«какие у них свойства и как они менялись»*
|
* 🟡 **Satellite (Сателлит)** — *«какие у них свойства и как они менялись»*
|
||||||
Атрибуты сущности (имя, email, статус, тариф) + история изменений.
|
Атрибуты сущности (имя, email, статус, тариф) плюс история изменений.
|
||||||
|
|
||||||
Главная идея:
|
Главная идея:
|
||||||
|
|
||||||
> **Идентичность, связи и атрибуты живут отдельно.**
|
> **Идентичность, связи и атрибуты живут отдельно.**
|
||||||
> Тогда изменения в одном не ломают другое.
|
> Тогда изменения в одном не ломают другое.
|
||||||
|
|
||||||
На картинке это выглядит примерно так:
|
На картинке это можно показать так:
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
erDiagram
|
erDiagram
|
||||||
HUB_CUSTOMER ||--o{ SAT_CUSTOMER_INFO : "имеет атрибуты"
|
HUB_CUSTOMER ||--o{ SAT_CUSTOMER_INFO : имеет_атрибуты
|
||||||
HUB_CUSTOMER ||--o{ LINK_ORDER_CUSTOMER : "участвует в"
|
HUB_CUSTOMER ||--o{ LINK_ORDER_CUSTOMER : участвует_в
|
||||||
HUB_ORDER ||--o{ LINK_ORDER_CUSTOMER : "создан"
|
HUB_ORDER ||--o{ LINK_ORDER_CUSTOMER : создан
|
||||||
LINK_ORDER_CUSTOMER ||--o{ SAT_ORDER_STATUS : "имеет историю статусов"
|
LINK_ORDER_CUSTOMER ||--o{ SAT_ORDER_STATUS : имеет_историю_статусов
|
||||||
|
|
||||||
HUB_CUSTOMER {
|
HUB_CUSTOMER {
|
||||||
varchar customer_bk PK "Бизнес-ключ: '101'"
|
varchar customer_bk PK
|
||||||
varchar record_source
|
varchar record_source
|
||||||
timestamp load_dttm
|
timestamp load_dttm
|
||||||
}
|
}
|
||||||
|
|
||||||
HUB_ORDER {
|
HUB_ORDER {
|
||||||
varchar order_id PK "Бизнес-ключ: '5001'"
|
varchar order_id PK
|
||||||
varchar record_source
|
varchar record_source
|
||||||
timestamp load_dttm
|
timestamp load_dttm
|
||||||
}
|
}
|
||||||
|
|
||||||
LINK_ORDER_CUSTOMER {
|
LINK_ORDER_CUSTOMER {
|
||||||
varchar link_key PK "Хэш (order_id + customer_bk)"
|
varchar link_key PK
|
||||||
varchar order_id FK
|
varchar order_id FK
|
||||||
varchar customer_bk FK
|
varchar customer_bk FK
|
||||||
varchar record_source
|
varchar record_source
|
||||||
@@ -109,11 +119,9 @@ erDiagram
|
|||||||
|
|
||||||
Как это читать:
|
Как это читать:
|
||||||
|
|
||||||
* 🔴 **HUB_CUSTOMER / HUB_ORDER** — «этот клиент существует», «этот заказ существует»;
|
* 🔴 HUB_CUSTOMER / HUB_ORDER — «этот клиент существует», «этот заказ существует»;
|
||||||
* ⚪ **LINK_ORDER_CUSTOMER** — «именно этот заказ сделал именно этот клиент»;
|
* ⚪ LINK_ORDER_CUSTOMER — «именно этот заказ сделал именно этот клиент»;
|
||||||
* 🟡 **SAT_…** — как менялись атрибуты (email, статус и т.п.) во времени.
|
* 🟡 SAT_… — как менялись атрибуты (email, статус и т.п.) во времени.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Три типа таблиц в Data Vault 2.0
|
## 3. Три типа таблиц в Data Vault 2.0
|
||||||
|
|
||||||
@@ -123,19 +131,19 @@ erDiagram
|
|||||||
|
|
||||||
**Hub** содержит:
|
**Hub** содержит:
|
||||||
|
|
||||||
* бизнес-ключ (`customer_bk`, `order_id`, `contract_number`);
|
* бизнес‑ключ (customer_bk, order_id, contract_number);
|
||||||
* техническую информацию:
|
* техническую информацию:
|
||||||
|
|
||||||
* `record_source` — из какой системы пришла первая запись;
|
* record_source — из какой системы пришла первая запись;
|
||||||
* `load_dttm` — когда попала в DV;
|
* load_dttm — когда запись попала в DV;
|
||||||
* иногда — хэш бизнес-ключа (`hk_customer`).
|
* иногда — хэш бизнес‑ключа (hk_customer).
|
||||||
|
|
||||||
Главные правила:
|
Главные правила:
|
||||||
|
|
||||||
* **один бизнес-ключ — один хаб** (одна строка на сущность, без истории);
|
* один бизнес‑ключ — один хаб (одна строка на сущность, без истории);
|
||||||
* хаб не знает про атрибуты (имя, email) — только идентичность.
|
* хаб не знает про атрибуты (имя, email) — только идентичность.
|
||||||
|
|
||||||
Простейший DDL-скелет:
|
Простейший DDL‑скелет:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE hub_customer (
|
CREATE TABLE hub_customer (
|
||||||
@@ -150,16 +158,16 @@ CREATE TABLE hub_customer (
|
|||||||
|
|
||||||
**Link** описывает факт связи, например:
|
**Link** описывает факт связи, например:
|
||||||
|
|
||||||
* заказ ↔ клиент,
|
* заказ ↔ клиент;
|
||||||
* договор ↔ счёт,
|
* договор ↔ счёт;
|
||||||
* карта ↔ клиент.
|
* карта ↔ клиент.
|
||||||
|
|
||||||
Примеры бизнес-смыслов:
|
Примеры бизнес‑смыслов:
|
||||||
|
|
||||||
* `link_order_customer` — «этот заказ принадлежит этому клиенту»;
|
* link_order_customer — «этот заказ принадлежит этому клиенту»;
|
||||||
* `link_contract_account` — «этот договор привязан к этому счёту».
|
* link_contract_account — «этот договор привязан к этому счёту».
|
||||||
|
|
||||||
DDL-приблизительно:
|
DDL‑эскиз:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE link_order_customer (
|
CREATE TABLE link_order_customer (
|
||||||
@@ -180,17 +188,17 @@ CREATE TABLE link_order_customer (
|
|||||||
|
|
||||||
Примеры:
|
Примеры:
|
||||||
|
|
||||||
* `sat_customer_info` — имя, email, город клиента;
|
* sat_customer_info — имя, email, город клиента;
|
||||||
* `sat_customer_segment` — сегмент, категория, риск-профиль;
|
* sat_customer_segment — сегмент, категория, риск‑профиль;
|
||||||
* `sat_order_status` — статус заказа.
|
* sat_order_status — статус заказа.
|
||||||
|
|
||||||
Типичные поля:
|
Типичные поля:
|
||||||
|
|
||||||
* ссылка на HUB/LINK (`hk_customer`, `hk_order_customer`);
|
* ссылка на HUB или LINK (hk_customer, hk_order_customer);
|
||||||
* атрибуты (email, city, status…);
|
* атрибуты (email, city, status и т.п.);
|
||||||
* `valid_from` / `valid_to` — период действия версии;
|
* valid_from / valid_to — период действия версии;
|
||||||
* `hashdiff` — хэш от всех атрибутов (чтобы понимать, что строка действительно изменилась, а не повторилась);
|
* hashdiff — хэш от всех атрибутов, чтобы понять, изменилась ли строка;
|
||||||
* `record_source`, `load_dttm`.
|
* record_source, load_dttm.
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE sat_customer_info (
|
CREATE TABLE sat_customer_info (
|
||||||
@@ -206,15 +214,15 @@ CREATE TABLE sat_customer_info (
|
|||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
Главная мысль: DV заставляет явно разделять идентичность, связи и атрибуты с историей. Это делает модель сложнее на вид, но гораздо устойчивее к изменениям источников.
|
||||||
|
|
||||||
## 4. Типы сателлитов в DV 2.0
|
## 4. Типы сателлитов в DV 2.0
|
||||||
|
|
||||||
В DV 2.0 появилось разделение по «ролям» сателлитов. Главное, что стоит знать:
|
В DV 2.0 появилось разделение по «ролям» сателлитов. Главное, что стоит знать:
|
||||||
|
|
||||||
* **Descriptive Satellites** — обычные атрибуты (имя, адрес, тариф) с историей.
|
* **Descriptive Satellites** — обычные атрибуты (имя, адрес, тариф) с историей.
|
||||||
* **Effectivity Satellites** — фокус на периодах действия (`valid_from/valid_to`), очень похоже на SCD2.
|
* **Effectivity Satellites** — фокус на периодах действия (`valid_from` / `valid_to`), очень похоже на SCD2.
|
||||||
* **Multi-Active Satellites** — когда у сущности **несколько одновременных** значений (например, три активных телефона клиента).
|
* **Multi-Active Satellites** — когда у сущности несколько одновременных значений (например, три активных телефона клиента).
|
||||||
* **Transactional Satellites** — события, привязанные к одному хабу/линку (например, журнал изменений статуса).
|
* **Transactional Satellites** — события, привязанные к одному хабу/линку (например, журнал изменений статуса).
|
||||||
|
|
||||||
На практике это разные DDL-«шаблоны» поверх одной и той же идеи:
|
На практике это разные DDL-«шаблоны» поверх одной и той же идеи:
|
||||||
@@ -222,69 +230,69 @@ CREATE TABLE sat_customer_info (
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. Raw Vault vs Business Vault: «склад деталей» и «сборочный цех»
|
## 5. Raw Vault и Business Vault
|
||||||
|
|
||||||
Обычно под «Data Vault» люди смешивают два слоя:
|
Обычно под «Data Vault» люди смешивают два слоя:
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
SRC[Источники] --> STG[STG / ODS]
|
SRC[Источники] --> STG[STG / ODS]
|
||||||
STG --> RAW[Raw Vault<br/>Hubs, Links, Sats]
|
STG --> RAW[Raw Vault
|
||||||
RAW --> BV[Business Vault<br/>PIT, Bridge, Derived]
|
Hubs, Links, Sats]
|
||||||
BV --> DM[Data Marts<br/>Star Schema]
|
RAW --> BV[Business Vault
|
||||||
DM --> BI[BI / reports / ML]
|
PIT, Bridge, Derived]
|
||||||
````
|
BV --> DM[Data Marts
|
||||||
|
Star Schema]
|
||||||
|
DM --> BI[BI / ML]
|
||||||
|
```
|
||||||
|
|
||||||
* **Raw Vault** — это *про приём и хранение* данных «как есть», но в форме Hub/Link/Sat.
|
* **Raw Vault** — это про приём и хранение данных «как есть», но уже в форме Hub / Link / Satellite.
|
||||||
* **Business Vault** — это *про приведение их в «деловой» вид*: с бизнес-правилами, PIT/Bridge и подготовленными представлениями.
|
* **Business Vault** — это про приведение этих данных в более «деловой» вид: с бизнес-правилами, PIT/Bridge и подготовленными представлениями.
|
||||||
|
|
||||||
### 5.1. Raw Vault — «всё прилетевшее, аккуратно разложенное по ящичкам»
|
### 5.1. Raw Vault — «всё прилетевшее, аккуратно разложенное по ящичкам»
|
||||||
|
|
||||||
**Raw DV** — первый слой поверх STG/ODS:
|
Raw DV — первый слой поверх STG / ODS:
|
||||||
|
|
||||||
* выравниваем ключи;
|
* выравниваем ключи;
|
||||||
* разбираем сущности по Hub / Link / Sat;
|
* разбираем сущности по Hub / Link / Sat;
|
||||||
* сохраняем *всю* историю изменений, не решая ещё «что такое активный клиент» или «успешный заказ».
|
* сохраняем всю историю изменений, не решая ещё, что такое «активный клиент» или «успешный заказ».
|
||||||
|
|
||||||
Характерные черты Raw Vault:
|
Характерные черты Raw Vault:
|
||||||
|
|
||||||
* минимум бизнес-логики:
|
* минимум бизнес-логики:
|
||||||
никаких «клиент активен, если было ≥1 покупки за 90 дней»;
|
|
||||||
|
* никаких правил вроде «клиент активен, если была хотя бы одна покупка за 90 дней»;
|
||||||
* все источники показываются «как есть», только приведены к общим ключам;
|
* все источники показываются «как есть», только приведены к общим ключам;
|
||||||
* структура стабильна: добавился новый источник → появился новый Sat к тому же Hub.
|
* структура стабильна: добавился новый источник → появился новый Satellite к тому же Hub.
|
||||||
|
|
||||||
Это слой **для инженеров**. Писать по нему отчёты — больно:
|
|
||||||
|
|
||||||
* чтобы узнать «какой у клиента email на дату заказа», нужно JOIN’ить Hub + Sat + Link, плюс фильтровать по `valid_from/valid_to` или `load_dttm`;
|
|
||||||
* чтобы собрать «портрет клиента» — нужно руками клеить несколько сателлитов.
|
|
||||||
|
|
||||||
Поэтому **Raw DV почти никогда не является точкой входа для аналитиков**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5.2. Business Vault — «там, где из Lego собирают модули»
|
### 5.2. Business Vault — «там, где из Lego собирают модули»
|
||||||
|
|
||||||
**Business Vault (BV)** — это слой поверх Raw DV, где:
|
Business Vault (BV) — следующий слой над Raw DV:
|
||||||
|
|
||||||
* появляются **бизнес-правила**,
|
* здесь применяются бизнес-правила (что считать активным клиентом, как трактовать статусы);
|
||||||
* строятся удобные для чтения **«сборные» объекты** (таблицы и представления),
|
* здесь строятся вспомогательные структуры:
|
||||||
* упрощается доступ к истории.
|
|
||||||
|
|
||||||
Кто потребитель BV:
|
* PIT-таблицы,
|
||||||
|
* Bridge-таблицы,
|
||||||
|
* агрегаты и derived-таблицы.
|
||||||
|
|
||||||
* разработчики витрин (DM / Star Schema);
|
Именно из BV чаще всего строятся витрины в формате Звезды, к которым подключаются BI и отчётность.
|
||||||
* часть сложных отчётов (особенно с тяжёлой историей);
|
|
||||||
* иногда — data scientists, если им нужен богатый, но ещё не «сплющенный» слой.
|
|
||||||
|
|
||||||
В BV живут несколько типичных конструкций.
|
Если сильно упростить:
|
||||||
|
|
||||||
|
* Raw DV → «мы всё собрали»;
|
||||||
|
* Business DV → «мы это привели в вид, с которым удобно жить»;
|
||||||
|
* DM → «мы вынесли это на витрину в понятной форме».
|
||||||
|
|
||||||
#### 5.2.1. PIT-таблицы (Point-in-Time)
|
#### 5.2.1. PIT-таблицы (Point-in-Time)
|
||||||
|
|
||||||
**PIT (Point-in-Time)** — таблицы, которые отвечают на вопрос:
|
PIT (Point-in-Time) отвечает на вопрос:
|
||||||
|
|
||||||
> «Как выглядел объект *на дату X*?»
|
> Как объект выглядел на дату X?
|
||||||
|
|
||||||
Вместо того чтобы каждый раз писать сложный запрос с диапазонами (`valid_from/valid_to`), мы заранее готовим таблицу:
|
Вместо того чтобы каждый раз писать сложные запросы с диапазонами `valid_from` / `valid_to`, мы заранее готовим таблицу с нужными ссылками на версии сателлитов.
|
||||||
|
|
||||||
|
Простейший эскиз:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE pit_customer_daily (
|
CREATE TABLE pit_customer_daily (
|
||||||
@@ -292,183 +300,188 @@ CREATE TABLE pit_customer_daily (
|
|||||||
as_of_date DATE,
|
as_of_date DATE,
|
||||||
hk_sat_info BYTEA, -- ссылка на нужную версию sat_customer_info
|
hk_sat_info BYTEA, -- ссылка на нужную версию sat_customer_info
|
||||||
hk_sat_segment BYTEA, -- ссылка на нужную версию sat_customer_segment
|
hk_sat_segment BYTEA, -- ссылка на нужную версию sat_customer_segment
|
||||||
...
|
|
||||||
load_dttm TIMESTAMP
|
load_dttm TIMESTAMP
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Теперь, чтобы собрать витрину продаж:
|
Теперь, чтобы собрать витрину продаж, достаточно один раз присоединить PIT по дате.
|
||||||
|
|
||||||
* JOIN `fact_orders` к `pit_customer_daily` по `order_date = as_of_date`;
|
|
||||||
* а уже потом — к сателлитам по их ключам.
|
|
||||||
|
|
||||||
Выигрыш:
|
|
||||||
|
|
||||||
* сложная логика выбора «правильной версии на дату» живёт в одном месте (ETL PIT);
|
|
||||||
* BI и витрины видят «почти плоский» слой.
|
|
||||||
|
|
||||||
#### 5.2.2. Bridge-таблицы
|
#### 5.2.2. Bridge-таблицы
|
||||||
|
|
||||||
**Bridge** — это «готовые маршруты» по графу Hub/Link.
|
Bridge-таблицы помогают проходить по сложным цепочкам связей:
|
||||||
|
|
||||||
Например:
|
* от клиента ко всем его договорам;
|
||||||
|
* от договора ко всем счетам;
|
||||||
|
* от счёта ко всем транзакциям.
|
||||||
|
|
||||||
* в DV клиент ↔ договор ↔ счёт ↔ продукт живут в разных хабах/линках;
|
Вместо того чтобы каждый раз писать длинный JOIN по нескольким Links, мы один раз строим Bridge и далее используем его как предрасчитанный путь.
|
||||||
* чтобы каждый раз не JOIN’ить весь путь, мы строим `bridge_customer_account`:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE TABLE bridge_customer_account AS
|
|
||||||
SELECT DISTINCT
|
|
||||||
hk_customer,
|
|
||||||
hk_account,
|
|
||||||
first_seen_dttm,
|
|
||||||
last_seen_dttm
|
|
||||||
FROM ...
|
|
||||||
```
|
|
||||||
|
|
||||||
Bridge:
|
|
||||||
|
|
||||||
* упрощают запросы для витрин;
|
|
||||||
* фиксируют сложные правила связи (например, что считать «текущим» счётом клиента).
|
|
||||||
|
|
||||||
#### 5.2.3. Business-правила и derived-таблицы
|
#### 5.2.3. Business-правила и derived-таблицы
|
||||||
|
|
||||||
В BV логично размещать бизнес-логику, которая:
|
В BV логично размещать бизнес-логику, которая:
|
||||||
|
|
||||||
* **повторяется** во многих отчётах;
|
* повторяется во многих отчётах;
|
||||||
* **стабильна** относительно конкретной витрины.
|
* достаточно стабильна.
|
||||||
|
|
||||||
Примеры:
|
Примеры:
|
||||||
|
|
||||||
* «Активный клиент» — флаг, который вычисляется на основе Raw DV (истории покупок, логинов и т.п.);
|
* флаг «активный клиент», который вычисляется на основе истории покупок и логинов;
|
||||||
* «Основной тариф» — выбран по набору правил из нескольких источников;
|
* «основной тариф», выбранный по набору правил из нескольких источников;
|
||||||
* «Чистый статус заказа» — свёрнут из цепочки статусов (created → paid → shipped → delivered / cancelled).
|
* «чистый статус заказа», свёрнутый из цепочки статусов (created → paid → shipped → delivered / cancelled).
|
||||||
|
|
||||||
Это могут быть как отдельные Sats/Links, так и «логические» таблицы BV:
|
Это могут быть как отдельные Satellites / Links, так и логические таблицы BV с уже посчитанными флагами и агрегатами.
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE TABLE bv_customer_flags AS
|
|
||||||
SELECT
|
|
||||||
hk_customer,
|
|
||||||
is_active_30d,
|
|
||||||
is_active_90d,
|
|
||||||
is_vip,
|
|
||||||
load_dttm
|
|
||||||
FROM ...
|
|
||||||
```
|
|
||||||
|
|
||||||
Потом витрины берут уже готовые флаги, не дублируя правила.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5.3. Граница между Business Vault и витринами (DM)
|
### 5.3. Граница между Business Vault и витринами (DM)
|
||||||
|
|
||||||
Важно не скатиться в две крайности:
|
Важно проговорить границу:
|
||||||
|
|
||||||
* всё тащить в Raw DV → BV пустой, витрины перегружены логикой;
|
* Business Vault — ещё про данные и историю;
|
||||||
* всё тащить в BV → витрины превращаются в тонкий слой SELECT’ов, но BV — новый «монолит».
|
* DM (Data Marts) — уже про конкретные бизнес-сценарии и удобство BI.
|
||||||
|
|
||||||
Полезное правило:
|
Витрины можно переделывать, не трогая BV, пока вы не меняете фундаментальные бизнес-правила.
|
||||||
|
|
||||||
> **В Business Vault живёт то, что относится к бизнес-сущностям и повторяется.
|
|
||||||
> В витринах живёт то, что уникально для конкретного отчёта/дашборда.**
|
|
||||||
|
|
||||||
Например:
|
|
||||||
|
|
||||||
* «клиент активен, если ≥1 покупки за 90 дней» — это логика уровня клиента → место ей в BV (`bv_customer_flags`);
|
|
||||||
* «клиент попал в эту конкретную маркетинговую воронку» — это логика конкретного отчёта → можно оставить в DM.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 5.4. Как выглядит связка Raw DV → BV → DM на примере
|
### 5.4. Как выглядит связка Raw DV → BV → DM на примере
|
||||||
|
|
||||||
Возьмём пример интернет-магазина:
|
Мини-пример интернет-магазина:
|
||||||
|
|
||||||
1. **Raw Vault**:
|
1. Raw Vault:
|
||||||
|
|
||||||
* `hub_customer`, `hub_order`;
|
* `hub_customer`, `hub_order`;
|
||||||
* `link_order_customer`;
|
* `link_order_customer`;
|
||||||
* `sat_customer_info`, `sat_order_status`, `sat_customer_segment`.
|
* `sat_customer_info`, `sat_order_status`, `sat_customer_segment`.
|
||||||
|
|
||||||
2. **Business Vault**:
|
2. Business Vault:
|
||||||
|
|
||||||
* `pit_customer_daily` — «срез клиента по дням»;
|
* `pit_customer_daily` — срез клиента по дням;
|
||||||
* `bv_customer_flags` — активность, VIP-статусы, сегменты;
|
* `bv_customer_flags` — активность, VIP-статусы, сегменты;
|
||||||
* `bridge_customer_order` — связи клиент ↔ заказ с удобными ключами.
|
* `bridge_customer_order` — связи клиент ↔ заказ с удобными ключами.
|
||||||
|
|
||||||
3. **DM / Star Schema**:
|
3. DM / Star Schema:
|
||||||
|
|
||||||
* `dm.fact_sales` — факт продаж;
|
* `dm.fact_sales` — факт продаж;
|
||||||
* `dm.dim_customer` — уже «плоское» измерение с нужными полями (`email_current`, `segment`, `is_active_90d`…);
|
* `dm.dim_customer` — уже плоское измерение с нужными полями (email_current, segment, is_active_90d и т.п.);
|
||||||
* `dm.dim_date`, `dm.dim_product` и т.п.
|
* `dm.dim_date`, `dm.dim_product` и другие измерения.
|
||||||
|
|
||||||
В результате:
|
В результате:
|
||||||
|
|
||||||
* **Raw DV** — технически правильный, историчный и некрасивый;
|
* Raw DV — технически корректный, историчный и некрасивый слой;
|
||||||
* **BV** — «рабочий слой для инженеров и продвинутых аналитиков»;
|
* BV — рабочий слой для инженеров и продвинутых аналитиков;
|
||||||
* **DM** — привычная Звезда для всех остальных.
|
* DM — привычная Звезда для всех остальных.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Кратко:
|
|
||||||
|
|
||||||
* Raw DV без BV — это как держать только структуры `hub_*/link_*/sat_*` и заставлять всех писать поверх них запросы. Это больно.
|
|
||||||
* **Business Vault — обязательный промежуточный слой**, если вы действительно живёте в Data Vault, а не просто «сложили историю по паттерну Hub/Link/Sat».
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Пример: клиент и заказы в DV-стиле
|
## 6. Пример: клиент и заказы в DV-стиле
|
||||||
|
|
||||||
Возьмём мини-пример (тот же интернет-магазин):
|
Возьмём мини‑пример всё того же интернет‑магазина.
|
||||||
|
|
||||||
* клиент с `customer_id = 101` и меняющимся email;
|
У нас есть:
|
||||||
* заказы `order_id = 5001, 5002`;
|
|
||||||
* статусы заказов.
|
|
||||||
|
|
||||||
В Data Vault:
|
* клиент с `customer_id = 101`, у которого иногда меняется email и город;
|
||||||
|
* два заказа: `order_id = 5001` и `order_id = 5002`;
|
||||||
|
* статусы заказов: `created → paid → shipped → delivered`.
|
||||||
|
|
||||||
* `hub_customer` — одна строка на BK `101`;
|
### Что появляется в Raw Vault
|
||||||
* `sat_customer_info` — несколько строк по мере смены email/города;
|
|
||||||
* `hub_order` — по строке на каждый заказ;
|
В Data Vault это раскладывается на несколько таблиц:
|
||||||
* `link_order_customer` — связь заказ ↔ клиент;
|
|
||||||
|
* `hub_customer` — по одной строке на каждого клиента (BK = `customer_id`).
|
||||||
|
* `hub_order` — по одной строке на каждый заказ (BK = `order_id`).
|
||||||
|
* `link_order_customer` — связь «какой заказ сделал какой клиент».
|
||||||
|
* `sat_customer_info` — история атрибутов клиента (имя, email, город).
|
||||||
* `sat_order_status` — история статусов заказа.
|
* `sat_order_status` — история статусов заказа.
|
||||||
|
|
||||||
Дальше:
|
Примерно так это выглядит логически:
|
||||||
|
|
||||||
* из Raw DV мы строим PIT:
|
```text
|
||||||
|
HUB_CUSTOMER
|
||||||
|
--------------------------------------
|
||||||
|
customer_bk | record_source | load_dttm
|
||||||
|
--------------------------------------
|
||||||
|
101 | CRM | 2023-01-10 10:00
|
||||||
|
|
||||||
* «каким был клиент на дату заказа?»;
|
SAT_CUSTOMER_INFO
|
||||||
* и уже из PIT + ссылок собираем витрину `mart_sales` в формате Звезды.
|
---------------------------------------------------------------------
|
||||||
|
customer_bk | valid_from | valid_to | email | city
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
101 | 2023-01-10 | 2023-06-01 | a@example.com | Moscow
|
||||||
|
101 | 2023-06-01 | 9999-12-31 | alice@newmail.com | Moscow
|
||||||
|
|
||||||
Тут важно, что **Raw DV почти не трогается** при изменении бизнес-логики — правим BV и витрины.
|
HUB_ORDER
|
||||||
|
--------------------------------------
|
||||||
|
order_id | record_source | load_dttm
|
||||||
|
--------------------------------------
|
||||||
|
5001 | SHOP | 2023-06-10 12:00
|
||||||
|
5002 | SHOP | 2023-06-11 09:30
|
||||||
|
|
||||||
---
|
SAT_ORDER_STATUS
|
||||||
|
----------------------------------------------------------------
|
||||||
|
order_id | valid_from | valid_to | status
|
||||||
|
----------------------------------------------------------------
|
||||||
|
5001 | 2023-06-10 | 2023-06-10 | created
|
||||||
|
5001 | 2023-06-10 | 2023-06-11 | paid
|
||||||
|
5001 | 2023-06-11 | 9999-12-31 | shipped
|
||||||
|
... | ... | ... | ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Ключевая идея: **любое изменение** (email, статус) — это **новая строка** в соответствующем Satellite.
|
||||||
|
|
||||||
|
### Как из этого получить витрину продаж
|
||||||
|
|
||||||
|
Дальше нам нужно привычное измерение `dim_customer` и факт `fact_sales` в формате Звезды.
|
||||||
|
|
||||||
|
Обычно цепочка выглядит так:
|
||||||
|
|
||||||
|
1. В Business Vault строим `PIT`‑таблицу по клиентам:
|
||||||
|
|
||||||
|
* для каждой даты (или дня, или часа) знаем, какая строка из `sat_customer_info` была актуальна.
|
||||||
|
2. Строим витрину `dm.dim_customer`:
|
||||||
|
|
||||||
|
* на выбранную дату берём нужную версию из `sat_customer_info`;
|
||||||
|
* добавляем флаги/сегменты из других Satellites/BV‑таблиц.
|
||||||
|
3. Строим факт `dm.fact_sales`, где каждая строка — заказ или позиция заказа.
|
||||||
|
|
||||||
|
Итог: на витрине мы видим «плоского» клиента (одна строка → текущее имя/город/email на момент заказа),
|
||||||
|
хотя внутри DV лежит полная, аккуратно разложенная история.
|
||||||
|
|
||||||
## 7. Как это живёт в пайплайне загрузки
|
## 7. Как это живёт в пайплайне загрузки
|
||||||
|
|
||||||
Типичный ETL/ELT с DV:
|
Посмотрим теперь, как DV вписывается в обычный ETL/ELT‑пайплайн.
|
||||||
|
|
||||||
1. **STG/ODS**: вытащили данные из источников, почистили типы, привели формат.
|
Типичный поток выглядит так:
|
||||||
2. **Raw Vault**:
|
|
||||||
|
|
||||||
* по BK вычислили хэш-ключи для Hubs;
|
1. **STG / ODS — приём и первичная обработка**
|
||||||
* создали/обновили Hubs;
|
|
||||||
* создали/обновили Links;
|
|
||||||
* сравнили `hashdiff` в Sats → добавили новые версии атрибутов.
|
|
||||||
3. **Business Vault**:
|
|
||||||
|
|
||||||
* собрали PIT-таблицы (одна строка на объект на дату X);
|
* Подключаемся к источникам (CRM, биллинг, сайт, партнёрские выгрузки).
|
||||||
* добавили бизнес-флаги и derived-поля.
|
* Забираем инкременты (CDC, выгрузки по расписанию, API).
|
||||||
4. **DM (Star)**:
|
* Приводим типы данных, чистим очевидный мусор, нормализуем форматы дат и т.п.
|
||||||
|
|
||||||
* построили факт + измерения, которые уже подходят BI/аналитикам.
|
2. **Raw Vault — приземление в Hub / Link / Satellite**
|
||||||
|
|
||||||
За счёт **хэш-ключей и hashdiff** DV хорошо масштабируется и параллелится:
|
* Из STG/ODS считаем хэш‑ключи для бизнес‑ключей (HK для Hubs).
|
||||||
разные сущности и домены могут грузиться независимыми пайплайнами.
|
* Создаём/обновляем **Hubs** — если бизнес‑ключ новый, заводим запись.
|
||||||
|
* Создаём/обновляем **Links** — фиксируем связи между сущностями.
|
||||||
|
* Для **Satellites** считаем `hashdiff` по атрибутам и добавляем новые строки
|
||||||
|
только если что‑то реально изменилось.
|
||||||
|
|
||||||
---
|
3. **Business Vault — бизнес‑правила и ускорители**
|
||||||
|
|
||||||
## 8. Плюсы и минусы Data Vault — без романтики
|
* Строим PIT‑таблицы, чтобы быстро получать срез «на дату X».
|
||||||
|
* Строим Bridge‑таблицы для сложных цепочек связей.
|
||||||
|
* Вычисляем стабильные бизнес‑флаги и derived‑атрибуты (активность, сегменты, статусы).
|
||||||
|
|
||||||
|
4. **DM / Star Schema — витрины для отчётов и аналитики**
|
||||||
|
|
||||||
|
* На основе BV собираем факты и измерения в формате Звезды.
|
||||||
|
* Под это уже настраиваются BI‑инструменты, отчётность, дашборды.
|
||||||
|
|
||||||
|
Чем это отличается от классического «STG → ODS → DDS → DM»:
|
||||||
|
|
||||||
|
* слой DDS в DV‑подходе часто фактически превращается в **Raw+Business Vault**;
|
||||||
|
* вместо одной «большой» нормализованной схемы ядра у нас набор Lego‑модулей (Hubs/Links/Sats);
|
||||||
|
* за счёт хэш‑ключей и независимой загрузки доменов пайплайны проще масштабировать
|
||||||
|
и параллелить по командам.
|
||||||
|
|
||||||
|
DV не отменяет STG/ODS/DM — он скорее **раскладывает слой DDS на более мелкие и управляемые детали**.
|
||||||
|
|
||||||
|
## 8. Плюсы и минусы Data Vault
|
||||||
|
|
||||||
### 8.1. Плюсы
|
### 8.1. Плюсы
|
||||||
|
|
||||||
@@ -487,7 +500,7 @@ FROM ...
|
|||||||
### 8.2. Минусы
|
### 8.2. Минусы
|
||||||
|
|
||||||
* 🧠 **Высокий порог входа**
|
* 🧠 **Высокий порог входа**
|
||||||
Нужно понимать SCD, хэш-ключи, нагрузку на JOIN, паттерны загрузки.
|
Нужно понимать SCD, хэш‑ключи, нагрузку на JOIN, паттерны загрузки.
|
||||||
|
|
||||||
* 📈 **Больше таблиц и JOIN’ов**
|
* 📈 **Больше таблиц и JOIN’ов**
|
||||||
Даже простой запрос превращается в «HUB + LINK + 2–3 SAT + PIT».
|
Даже простой запрос превращается в «HUB + LINK + 2–3 SAT + PIT».
|
||||||
@@ -498,9 +511,7 @@ FROM ...
|
|||||||
* ⏳ **Плохо подходит для MVP**
|
* ⏳ **Плохо подходит для MVP**
|
||||||
Для 2–3 источников DV обычно дороже, чем классическая 3NF/Звезда.
|
Для 2–3 источников DV обычно дороже, чем классическая 3NF/Звезда.
|
||||||
|
|
||||||
---
|
## 9. Когда DV стоит использовать, а когда нет
|
||||||
|
|
||||||
## 9. Когда DV стоит использовать, а когда — нет
|
|
||||||
|
|
||||||
### Подходит, если:
|
### Подходит, если:
|
||||||
|
|
||||||
@@ -521,32 +532,3 @@ FROM ...
|
|||||||
> `stg → ods → dds (3NF/простая Звезда с SCD2) → dm (Звезда)`
|
> `stg → ods → dds (3NF/простая Звезда с SCD2) → dm (Звезда)`
|
||||||
|
|
||||||
А DV оставить как следующий шаг, когда появятся реальные боли, которые он решает.
|
А DV оставить как следующий шаг, когда появятся реальные боли, которые он решает.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. Как учить Data Vault дальше
|
|
||||||
|
|
||||||
Если после этой статьи хочется «копнуть глубже», можно идти по такой траектории:
|
|
||||||
|
|
||||||
1. **Повторить базу**:
|
|
||||||
|
|
||||||
* слои STG/ODS/DDS/DM;
|
|
||||||
* факты/измерения;
|
|
||||||
* SCD Type 2.
|
|
||||||
|
|
||||||
2. **Почитать/посмотреть про DV 2.0**:
|
|
||||||
|
|
||||||
* книги/доки Линстедта (Dan Linstedt, *Data Vault 2.0*),
|
|
||||||
* практические доклады (особенно от телекомов и банков — там DV живой).
|
|
||||||
|
|
||||||
3. **Сделать игрушечный DV-проект**:
|
|
||||||
|
|
||||||
* взять тот же интернет-магазин,
|
|
||||||
* смоделировать `hub_customer`, `hub_order`, `link_order_customer`, 2–3 сателлита,
|
|
||||||
* написать пару запросов: «какой был клиент на дату заказа?», «как менялся статус заказа?».
|
|
||||||
|
|
||||||
4. **Посмотреть на гибриды**:
|
|
||||||
|
|
||||||
* DV в ядре (Raw+Business Vault),
|
|
||||||
* Звезда на витринах.
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user