Доработки идей
This commit is contained in:
+63
-27
@@ -433,27 +433,71 @@ GROUP BY d.date_actual, p.product_name, c.customer_segment;
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **8. Как выбрать модель? Дерево решений**
|
## **8. Как выбрать модель данных? Советы от практиков**
|
||||||
|
|
||||||
Вот простой алгоритм:
|
Выбор модели — **не техническая задача, а стратегическая**.
|
||||||
|
Это как решать: строить дом из кирпича, дерева или SIP-панелей. У каждой технологии — свои плюсы, но **главное — подходит ли она *вам* сегодня**.
|
||||||
|
|
||||||
```
|
### 🔹 Главное, что нужно понять новичку:
|
||||||
Нужна ли история изменений? ── Нет → Звезда (просто и быстро)
|
|
||||||
│
|
|
||||||
Да
|
|
||||||
│
|
|
||||||
Много ли источников (>5)? ── Нет → 3NF (надёжно, понятно)
|
|
||||||
│
|
|
||||||
Да
|
|
||||||
│
|
|
||||||
Нужна ли аудиторская трассировка? ── Нет → 3NF + SCD
|
|
||||||
│
|
|
||||||
Да → Data Vault
|
|
||||||
```
|
|
||||||
|
|
||||||
> 🛑 Не делайте «гибрид» без причины:
|
> **Не существует «самой правильной» модели.**
|
||||||
> — Звезда в DDS — плохо (потеряете гибкость);
|
> Есть **самая подходящая под ваш контекст** — и он у всех разный.
|
||||||
> — DV в DM — плохо (BI не потянет 50 JOIN’ов).
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🛑 Что делать **не стоит** (если опыта < 2 лет в DWH):
|
||||||
|
|
||||||
|
| Что делать не стоит | Почему |
|
||||||
|
|---------------------|--------|
|
||||||
|
| **Брать Data Vault «потому что модно»** | DV требует глубокого понимания интеграции, CDC, идемпотентности. Без этого — получите «историю», где невозможно найти актуальные данные. |
|
||||||
|
| **Строить сложную 3NF «как в книжках» под 10 таблиц** | Если у вас 2 источника — вы потратите 2 недели на нормализацию, чтобы потом делать 5 JOIN’ов ради простого отчёта. |
|
||||||
|
| **Пытаться «сделать сразу гибко на 5 лет вперёд»** | Гибкость = сложность. А сложность = баги, задержки, выгорание команды. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ✅ **Базовые советы — с чего начать, если вы учитесь или делаете первый DWH**
|
||||||
|
|
||||||
|
1. **Начните с витрины в формате Звезды (Star Schema).**
|
||||||
|
— Это просто: одна таблица фактов + несколько «плоских» справочников.
|
||||||
|
— Это быстро: отчёт в Power BI — за 10 минут.
|
||||||
|
— Это понятно: даже менеджер поймёт структуру.
|
||||||
|
|
||||||
|
2. **Стройте DDS только когда это *действительно нужно*.**
|
||||||
|
— Если источников ≤ 3 и они стабильны — можно `ods → dm` напрямую.
|
||||||
|
— Если появятся расхождения («email в CRM и в заказах — разные»), *тогда* заводите `dds.dim_customer`.
|
||||||
|
|
||||||
|
3. **Историю (SCD) включайте *постепенно*.**
|
||||||
|
— Сначала — без истории (Type 1: просто обновляете строку).
|
||||||
|
— Потом — только для ключевых сущностей (клиент, товар).
|
||||||
|
— Только потом — думайте про DV или полную историзацию.
|
||||||
|
|
||||||
|
4. **Если сомневаетесь — спросите: «А кто будет этим пользоваться?»**
|
||||||
|
— Аналитик в Metabase → Звезда.
|
||||||
|
— BI-разработчик в Power BI → Звезда.
|
||||||
|
— Инженер, который строит ML-фичи → 3NF или даже сырые ODS-таблицы.
|
||||||
|
— Аудитор или регулятор → DV (но только если вы *готовы* к его сложности).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 💡 И ещё один совет от практиков:
|
||||||
|
|
||||||
|
> **Лучше сделать простую модель — и вовремя переделать,**
|
||||||
|
> чем сделать «идеальную» — и застрять на этапе проектирования.
|
||||||
|
|
||||||
|
Переделать Звезду → Звезда с SCD Type 2 — легко.
|
||||||
|
Переделать «недоделанный DV» → что-то рабочее — в 10 раз сложнее.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📌 Кратко — что выбрать *сегодня*, если вы только учитесь:
|
||||||
|
|
||||||
|
| У вас… | Делайте… |
|
||||||
|
|--------|----------|
|
||||||
|
| Учебный проект, 1–2 CSV | `ods → dm` по модели **Звезда** (без DDS, без истории) |
|
||||||
|
| Первый рабочий DWH, 3–5 источников | `stg → ods → dds (3NF/простая Звезда) → dm (Звезда)` |
|
||||||
|
| Команда из 1 инженера + 1 аналитика | **Не трогайте DV и Anchor** — они «съедят» ваше время без отдачи |
|
||||||
|
|
||||||
|
А когда наберётесь опыта — приходите в DV. Он того стоит. Но *не раньше времени*.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -621,7 +665,7 @@ product_id,valid_from,valid_to,price
|
|||||||
9001,2024-02-01,2999-12-31,110
|
9001,2024-02-01,2999-12-31,110
|
||||||
```
|
```
|
||||||
|
|
||||||
> 📂 Примеры DDL, SQL-загрузки, SCD-скрипты — в [GitHub-репозитории к статье](https://github.com/yourname/dwh-basics) (реальный репо — по вашему усмотрению).
|
> 📂 Примеры DDL, SQL-загрузки, SCD-скрипты — в [GitHub-репозитории к статье](https://github.com/dementev-dev/de-roadmap/).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -653,11 +697,3 @@ CREATE TABLE dds.fact_sales (
|
|||||||
|
|
||||||
> 💡 `date_key` — это `20240110`, а не `DATE`, чтобы не делать JOIN по диапазону в `fact → dim_date`.
|
> 💡 `date_key` — это `20240110`, а не `DATE`, чтобы не делать JOIN по диапазону в `fact → dim_date`.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Если нужно — могу:
|
|
||||||
- добавить **интерактивные схемы** (например, кликабельные Mermaid → SVG);
|
|
||||||
- подготовить **полный SQL-скрипт загрузки** (STG→ODS→DDS);
|
|
||||||
- сделать **вариант статьи в формате Jupyter Notebook** (с исполняемыми ячейками).
|
|
||||||
|
|
||||||
Готов дорабатывать под ваш стиль и аудиторию.
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
-- ===============================================
|
||||||
|
-- DDL: Data Marts (DM)
|
||||||
|
-- Слой "готовых решений" — для BI, отчётов, API
|
||||||
|
-- ===============================================
|
||||||
|
|
||||||
|
DROP SCHEMA IF EXISTS dm CASCADE;
|
||||||
|
CREATE SCHEMA dm;
|
||||||
|
|
||||||
|
-- Витрина: ежедневные продажи по товарам и клиентам
|
||||||
|
-- Гранулярность: 1 строка = 1 день × 1 товар × 1 сегмент клиента
|
||||||
|
CREATE TABLE dm.mart_daily_sales (
|
||||||
|
date_actual DATE NOT NULL,
|
||||||
|
product_name VARCHAR(100) NOT NULL,
|
||||||
|
customer_segment VARCHAR(20) NOT NULL, -- напр. 'Premium', 'Basic'
|
||||||
|
total_qty INT NOT NULL,
|
||||||
|
total_revenue NUMERIC(18,2) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Витрина: 360°-портрет клиента (lifetime value)
|
||||||
|
CREATE TABLE dm.mart_customer_360 (
|
||||||
|
customer_bk INT NOT NULL,
|
||||||
|
first_order_date DATE,
|
||||||
|
last_order_date DATE,
|
||||||
|
total_orders INT NOT NULL,
|
||||||
|
total_items INT NOT NULL,
|
||||||
|
lifetime_value NUMERIC(18,2) NOT NULL,
|
||||||
|
last_email VARCHAR(100),
|
||||||
|
last_city VARCHAR(50)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Индексы для ускорения BI (опционально, но рекомендовано)
|
||||||
|
CREATE INDEX ON dm.mart_daily_sales (date_actual);
|
||||||
|
CREATE INDEX ON dm.mart_daily_sales (product_name);
|
||||||
|
CREATE INDEX ON dm.mart_customer_360 (customer_bk);
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
-- ===============================================
|
||||||
|
-- DML: построение Data Marts из DDS
|
||||||
|
-- Идемпотентно: можно пересобирать в любое время
|
||||||
|
-- ===============================================
|
||||||
|
|
||||||
|
-- 1. Очистка (full refresh — для простоты; в продакшене — incremental)
|
||||||
|
TRUNCATE dm.mart_daily_sales, dm.mart_customer_360;
|
||||||
|
|
||||||
|
-- 2. mart_daily_sales: агрегация по дням
|
||||||
|
-- Используем актуальные версии измерений (is_current = true)
|
||||||
|
-- Для кого: Маркетолог, продакт-аналитик
|
||||||
|
-- Пример использования: «Как менялись продажи Phone в сегменте Premium по дням?»
|
||||||
|
INSERT INTO dm.mart_daily_sales (
|
||||||
|
date_actual, product_name, customer_segment, total_qty, total_revenue
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
d.date_actual,
|
||||||
|
p.product_name,
|
||||||
|
-- Простая сегментация по выручке за заказ
|
||||||
|
CASE
|
||||||
|
WHEN f.amount >= 200 THEN 'Premium'
|
||||||
|
ELSE 'Basic'
|
||||||
|
END AS customer_segment,
|
||||||
|
SUM(f.quantity) AS total_qty,
|
||||||
|
SUM(f.amount) AS total_revenue
|
||||||
|
FROM dds.fact_sales f
|
||||||
|
JOIN dds.dim_date d ON f.date_key = d.date_key
|
||||||
|
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 c.is_current
|
||||||
|
GROUP BY d.date_actual, p.product_name,
|
||||||
|
CASE WHEN f.amount >= 200 THEN 'Premium' ELSE 'Basic' END;
|
||||||
|
|
||||||
|
-- 3. mart_customer_360: lifetime-портрет
|
||||||
|
-- Здесь — НЕ используем is_current! Нам нужна вся история для расчёта LTV
|
||||||
|
-- Для кого: CRM-менеджер, retention-спец
|
||||||
|
-- Пример использования: «Найти клиентов с LTV > 250 ₽ и email из Москвы для email-рассылки»
|
||||||
|
«Найти клиентов с LTV > 250 ₽ и email из Москвы для email-рассылки»
|
||||||
|
INSERT INTO dm.mart_customer_360 (
|
||||||
|
customer_bk, first_order_date, last_order_date,
|
||||||
|
total_orders, total_items, lifetime_value,
|
||||||
|
last_email, last_city
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
c.customer_bk,
|
||||||
|
MIN(d.date_actual) AS first_order_date,
|
||||||
|
MAX(d.date_actual) AS last_order_date,
|
||||||
|
COUNT(DISTINCT f.sale_id) AS total_orders,
|
||||||
|
SUM(f.quantity) AS total_items,
|
||||||
|
SUM(f.amount) AS lifetime_value,
|
||||||
|
-- Берём email и город из самой свежей версии клиента
|
||||||
|
(SELECT email FROM dds.dim_customer c2
|
||||||
|
WHERE c2.customer_bk = c.customer_bk
|
||||||
|
ORDER BY c2.valid_from DESC
|
||||||
|
LIMIT 1) AS last_email,
|
||||||
|
(SELECT city FROM dds.dim_customer c2
|
||||||
|
WHERE c2.customer_bk = c.customer_bk
|
||||||
|
ORDER BY c2.valid_from DESC
|
||||||
|
LIMIT 1) AS last_city
|
||||||
|
FROM dds.fact_sales f
|
||||||
|
JOIN dds.dim_date d ON f.date_key = d.date_key
|
||||||
|
JOIN dds.dim_customer c ON f.customer_sk = c.customer_sk
|
||||||
|
-- ⚠️ Важно: здесь НЕ фильтруем по is_current — нужна вся история!
|
||||||
|
GROUP BY c.customer_bk;
|
||||||
|
|
||||||
|
-- 4. Дополнительно: материализованное представление (альтернатива таблице)
|
||||||
|
-- В PG 12+ можно использовать MATERIALIZED VIEW — обновляется по команде REFRESH
|
||||||
|
-- CREATE MATERIALIZED VIEW dm.mv_monthly_sales AS
|
||||||
|
-- SELECT ... (аналогично)
|
||||||
Reference in New Issue
Block a user