Переименования скриптов
This commit is contained in:
@@ -1,255 +0,0 @@
|
||||
-- ===============================================
|
||||
-- 1. Создание схем
|
||||
-- ===============================================
|
||||
DROP SCHEMA IF EXISTS stg CASCADE;
|
||||
DROP SCHEMA IF EXISTS ods CASCADE;
|
||||
DROP SCHEMA IF EXISTS dds CASCADE;
|
||||
CREATE SCHEMA stg;
|
||||
CREATE SCHEMA ods;
|
||||
CREATE SCHEMA dds;
|
||||
|
||||
-- ===============================================
|
||||
-- 2. STG: таблицы «как пришло»
|
||||
-- ===============================================
|
||||
|
||||
-- Сырые данные: строки, как из CSV/API
|
||||
CREATE TABLE stg.customers_raw (
|
||||
customer_id TEXT, -- может быть '101' или 'CUST-101'
|
||||
email TEXT,
|
||||
phone TEXT,
|
||||
city TEXT,
|
||||
_load_ts TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE stg.orders_raw (
|
||||
order_id TEXT,
|
||||
order_date TEXT, -- формат: '2024-01-10'
|
||||
customer_id TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE stg.order_items_raw (
|
||||
order_item_id TEXT,
|
||||
order_id TEXT,
|
||||
product_id TEXT,
|
||||
qty TEXT, -- может быть '2', 'NULL'
|
||||
price_at_sale TEXT -- может быть '100.00'
|
||||
);
|
||||
|
||||
CREATE TABLE stg.products_raw (
|
||||
product_id TEXT,
|
||||
name TEXT
|
||||
);
|
||||
|
||||
-- Загрузка тестовых данных (вместо COPY FROM CSV)
|
||||
INSERT INTO stg.customers_raw (customer_id, email, phone, city) VALUES
|
||||
('101', 'a@ex.com', '700', 'Москва'),
|
||||
('101', 'b@ex.com', '700', 'Москва'), -- изменение email
|
||||
('102', 'c@ex.com', '701', 'СПб');
|
||||
|
||||
INSERT INTO stg.orders_raw (order_id, order_date, customer_id) VALUES
|
||||
('5001', '2024-01-10', '101'),
|
||||
('5002', '2024-02-05', '102');
|
||||
|
||||
INSERT INTO stg.order_items_raw (order_item_id, order_id, product_id, qty, price_at_sale) VALUES
|
||||
('1', '5001', '9001', '2', '100.00'),
|
||||
('2', '5001', '9002', '1', '50.00'),
|
||||
('3', '5002', '9001', '1', '100.00');
|
||||
|
||||
INSERT INTO stg.products_raw (product_id, name) VALUES
|
||||
('9001', 'Phone'),
|
||||
('9002', 'Case');
|
||||
|
||||
-- ===============================================
|
||||
-- 3. ODS: очистка и типизация
|
||||
-- ===============================================
|
||||
|
||||
-- Очищенные клиенты
|
||||
CREATE TABLE ods.customers AS
|
||||
SELECT
|
||||
customer_id::INT AS customer_id, -- приведение к INT
|
||||
NULLIF(TRIM(email), '') AS email, -- пустые → NULL
|
||||
NULLIF(TRIM(phone), '') AS phone,
|
||||
NULLIF(TRIM(city), '') AS city
|
||||
FROM stg.customers_raw
|
||||
WHERE customer_id ~ '^\d+$'; -- валидация: только цифры
|
||||
|
||||
-- Очищенные заказы
|
||||
CREATE TABLE ods.orders AS
|
||||
SELECT
|
||||
order_id::INT,
|
||||
TO_DATE(order_date, 'YYYY-MM-DD') AS order_date,
|
||||
customer_id::INT
|
||||
FROM stg.orders_raw
|
||||
WHERE order_date IS NOT NULL AND customer_id ~ '^\d+$';
|
||||
|
||||
-- Очищенные позиции заказа
|
||||
CREATE TABLE ods.order_items AS
|
||||
SELECT
|
||||
order_item_id::INT,
|
||||
order_id::INT,
|
||||
product_id::INT,
|
||||
NULLIF(qty, '')::INT AS qty,
|
||||
NULLIF(price_at_sale, '')::NUMERIC(10,2) AS price_at_sale
|
||||
FROM stg.order_items_raw
|
||||
WHERE qty ~ '^\d+$' AND price_at_sale ~ '^\d+(\.\d+)?$';
|
||||
|
||||
-- Очищенные товары
|
||||
CREATE TABLE ods.products AS
|
||||
SELECT
|
||||
product_id::INT,
|
||||
TRIM(name) AS name
|
||||
FROM stg.products_raw
|
||||
WHERE product_id ~ '^\d+$';
|
||||
|
||||
-- Добавим первичные ключи (для ускорения JOIN и проверки)
|
||||
ALTER TABLE ods.customers ADD PRIMARY KEY (customer_id);
|
||||
ALTER TABLE ods.orders ADD PRIMARY KEY (order_id);
|
||||
ALTER TABLE ods.order_items ADD PRIMARY KEY (order_item_id);
|
||||
ALTER TABLE ods.products ADD PRIMARY KEY (product_id);
|
||||
|
||||
-- ===============================================
|
||||
-- 4. DDS: интеграция + SCD Type 2
|
||||
-- ===============================================
|
||||
|
||||
-- 4.1 dim_date — справочник дат (на 5 лет: 2023–2027)
|
||||
CREATE TABLE dds.dim_date AS
|
||||
WITH RECURSIVE dates AS (
|
||||
SELECT DATE '2023-01-01' AS d
|
||||
UNION ALL
|
||||
SELECT d + INTERVAL '1 day'
|
||||
FROM dates
|
||||
WHERE d + INTERVAL '1 day' <= DATE '2027-12-31'
|
||||
)
|
||||
SELECT
|
||||
CAST(TO_CHAR(d, 'YYYYMMDD') AS INT) AS date_key,
|
||||
d AS date_actual,
|
||||
EXTRACT(YEAR FROM d) AS year,
|
||||
EXTRACT(QUARTER FROM d) AS quarter,
|
||||
EXTRACT(MONTH FROM d) AS month,
|
||||
EXTRACT(DAY FROM d) AS day,
|
||||
TO_CHAR(d, 'Day') AS weekday_name,
|
||||
EXTRACT(DOW FROM d) AS weekday_num, -- 0 = воскресенье
|
||||
d BETWEEN
|
||||
DATE_TRUNC('month', d)
|
||||
AND DATE_TRUNC('month', d) + INTERVAL '1 month' - INTERVAL '1 day'
|
||||
AND EXTRACT(DAY FROM d) <= 7
|
||||
AS is_first_week
|
||||
FROM dates;
|
||||
|
||||
ALTER TABLE dds.dim_date ADD PRIMARY KEY (date_key);
|
||||
|
||||
-- 4.2 dim_product — измерение «Товар» (без истории — атрибуты стабильны)
|
||||
CREATE TABLE dds.dim_product (
|
||||
product_sk BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
product_bk INT NOT NULL,
|
||||
product_name VARCHAR(100) NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO dds.dim_product (product_bk, product_name)
|
||||
SELECT product_id, name
|
||||
FROM ods.products;
|
||||
|
||||
-- 4.3 dim_customer — SCD Type 2 (с историей)
|
||||
CREATE TABLE dds.dim_customer (
|
||||
customer_sk BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
customer_bk INT NOT NULL,
|
||||
email VARCHAR(100),
|
||||
phone VARCHAR(20),
|
||||
city VARCHAR(50),
|
||||
valid_from DATE NOT NULL,
|
||||
valid_to DATE NOT NULL DEFAULT '9999-12-31',
|
||||
is_current BOOLEAN NOT NULL DEFAULT TRUE
|
||||
);
|
||||
|
||||
-- Загрузка первой версии (SCD Type 2 — простой алгоритм)
|
||||
-- Группируем по customer_bk и сортируем по времени (по _load_ts из STG)
|
||||
-- Для учебного примера имитируем хронологию через порядок строк
|
||||
WITH ranked AS (
|
||||
SELECT
|
||||
customer_id AS customer_bk,
|
||||
email,
|
||||
phone,
|
||||
city,
|
||||
ROW_NUMBER() OVER (
|
||||
PARTITION BY customer_id
|
||||
ORDER BY _load_ts, email -- хронология изменений
|
||||
) AS rn
|
||||
FROM stg.customers_raw
|
||||
WHERE customer_id ~ '^\d+$'
|
||||
),
|
||||
changes AS (
|
||||
SELECT
|
||||
customer_bk,
|
||||
email,
|
||||
phone,
|
||||
city,
|
||||
-- начальная дата — день первого появления в источнике
|
||||
'2023-01-01'::DATE + (rn - 1) * INTERVAL '1 day' AS eff_from
|
||||
FROM ranked
|
||||
),
|
||||
final AS (
|
||||
SELECT
|
||||
customer_bk,
|
||||
email,
|
||||
phone,
|
||||
city,
|
||||
eff_from::DATE AS valid_from,
|
||||
COALESCE(
|
||||
LEAD(eff_from) OVER (PARTITION BY customer_bk ORDER BY eff_from) - INTERVAL '1 day',
|
||||
'9999-12-31'::DATE
|
||||
) AS valid_to,
|
||||
CASE WHEN
|
||||
LEAD(eff_from) OVER (PARTITION BY customer_bk ORDER BY eff_from) IS NULL
|
||||
THEN TRUE ELSE FALSE END AS is_current
|
||||
FROM changes
|
||||
)
|
||||
INSERT INTO dds.dim_customer (customer_bk, email, phone, city, valid_from, valid_to, is_current)
|
||||
SELECT customer_bk, email, phone, city, valid_from, valid_to, is_current
|
||||
FROM final;
|
||||
|
||||
-- 4.4 fact_sales — факт продаж
|
||||
CREATE TABLE dds.fact_sales (
|
||||
sale_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
customer_sk BIGINT NOT NULL, -- ссылка на DDS-измерение
|
||||
product_sk BIGINT NOT NULL,
|
||||
date_key INT NOT NULL, -- YYYYMMDD
|
||||
quantity INT NOT NULL CHECK (quantity > 0),
|
||||
amount NUMERIC(18,2) NOT NULL CHECK (amount >= 0)
|
||||
);
|
||||
|
||||
-- Заполнение fact_sales с учётом истории клиента (SCD!)
|
||||
INSERT INTO dds.fact_sales (customer_sk, product_sk, date_key, quantity, amount)
|
||||
SELECT
|
||||
dc.customer_sk,
|
||||
dp.product_sk,
|
||||
CAST(TO_CHAR(o.order_date, 'YYYYMMDD') AS INT) AS date_key,
|
||||
oi.qty,
|
||||
oi.price_at_sale * oi.qty AS amount
|
||||
FROM ods.orders o
|
||||
JOIN ods.order_items oi ON o.order_id = oi.order_id
|
||||
JOIN ods.products p ON oi.product_id = p.product_id
|
||||
JOIN dds.dim_product dp ON p.product_id = dp.product_bk
|
||||
JOIN dds.dim_customer dc
|
||||
ON o.customer_id = dc.customer_bk
|
||||
AND o.order_date BETWEEN dc.valid_from AND dc.valid_to; -- ← ключевая строка SCD!
|
||||
|
||||
-- Добавим внешние ключи (опционально, для целостности)
|
||||
ALTER TABLE dds.fact_sales
|
||||
ADD FOREIGN KEY (customer_sk) REFERENCES dds.dim_customer(customer_sk),
|
||||
ADD FOREIGN KEY (product_sk) REFERENCES dds.dim_product(product_sk),
|
||||
ADD FOREIGN KEY (date_key) REFERENCES dds.dim_date(date_key);
|
||||
|
||||
-- ===============================================
|
||||
-- 5. Проверка: посчитаем выручку по клиентам
|
||||
-- ===============================================
|
||||
SELECT
|
||||
dc.customer_bk,
|
||||
dc.email,
|
||||
dc.city,
|
||||
SUM(f.amount) AS total_revenue
|
||||
FROM dds.fact_sales f
|
||||
JOIN dds.dim_customer dc
|
||||
ON f.customer_sk = dc.customer_sk
|
||||
AND dc.is_current -- только актуальная версия
|
||||
GROUP BY dc.customer_bk, dc.email, dc.city
|
||||
ORDER BY total_revenue DESC;
|
||||
@@ -1,288 +0,0 @@
|
||||
# Структура хранилища данных (для студентов SQL/Postgres)
|
||||
|
||||
> **Цель**: объяснить, как раскладывать данные в аналитической БД и почему именно так принято. Рус/Eng термины приводятся вместе (например, «витрина (Data Mart)»).
|
||||
Статья для учениковп про то, чтобы понимать модели и уметь пользоваться моделированнаыми хранилищами, знать их структуру. У нее нет цели научить проектировать хранилища. Цель - научить понимать структуру и ее смысл
|
||||
|
||||
---
|
||||
|
||||
## 0. Навигация по статье
|
||||
|
||||
* [1. Введение: зачем слои и почему не «всё в одну таблицу»](#1)
|
||||
* [2. Учебный домен/пример](#2)
|
||||
* [3. Типичная структура слоёв (STG → ODS → DDS → DM)](#3)
|
||||
* [4. Слои по отдельности](#4)
|
||||
* [5. Модели данных для DDS/DM: 3NF, Звезда/Снежинка, Data Vault, Anchor](#5)
|
||||
* [6. Ключевые понятия: факт/измерение, зерно, SK vs BK, SCD](#6)
|
||||
* [7. Выбор подхода: дерево решений](#7)
|
||||
* [8. Эволюция схемы и эксплуатации](#8)
|
||||
* [9. MVP учебного проекта](#9)
|
||||
* [10. Заключение](#10)
|
||||
|
||||
---
|
||||
|
||||
## 1. Введение: зачем слои и почему не «всё в одну таблицу»
|
||||
|
||||
**Коротко**: OLTP vs OLAP, управляемость, прозрачность, стоимость.
|
||||
|
||||
**Заглушки для текста**:
|
||||
|
||||
* Что такое слои и какую проблему решают.
|
||||
* Почему «одна большая таблица» ломается на истории и изменениях.
|
||||
* 3–4 тезиса о выгодах послойной архитектуры.
|
||||
|
||||
---
|
||||
|
||||
## 2. Учебный домен/пример (интернет-магазин)
|
||||
|
||||
**Описание**: используем один домен сквозь статью: клиенты, товары, заказы, позиции заказа, цены, акции.
|
||||
|
||||
**Диаграмма ER (эскиз)**:
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
ORDER ||--|{ ORDER_ITEM : contains
|
||||
PRODUCT ||--o{ ORDER_ITEM : referenced
|
||||
PRODUCT ||--o{ PRICE : has
|
||||
PROMO ||--o{ ORDER_ITEM : applied
|
||||
CUSTOMER {
|
||||
int customer_id PK
|
||||
string email
|
||||
string phone
|
||||
}
|
||||
PRODUCT {
|
||||
int product_id PK
|
||||
string name
|
||||
}
|
||||
ORDER {
|
||||
int order_id PK
|
||||
date order_date
|
||||
int customer_id FK
|
||||
}
|
||||
ORDER_ITEM {
|
||||
int order_item_id PK
|
||||
int order_id FK
|
||||
int product_id FK
|
||||
int qty
|
||||
numeric price_at_sale
|
||||
}
|
||||
PRICE {
|
||||
int product_id FK
|
||||
date valid_from
|
||||
date valid_to
|
||||
numeric price
|
||||
}
|
||||
PROMO {
|
||||
int promo_id PK
|
||||
string code
|
||||
}
|
||||
```
|
||||
|
||||
**Заглушки**: 2–3 абзаца с пояснениями domain-гранулярности и бизнес-ключей (BK).
|
||||
|
||||
---
|
||||
|
||||
## 3. Типичная структура слоёв (STG → ODS → DDS → DM)
|
||||
|
||||
**Картинка-конвейер (эскиз)**:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Sources[Источники]
|
||||
A[CRM] --> STG
|
||||
B[Billing] --> STG
|
||||
C[E-comm] --> STG
|
||||
end
|
||||
STG[STG<br/>Staging / Bronze] --> ODS[ODS<br/>Operational / Silver]
|
||||
ODS --> DDS[DDS<br/>Integrated / Conformed]
|
||||
DDS --> DM[DM<br/>Data Marts / Gold]
|
||||
DM --> BI[BI / Отчёты / Дашборды]
|
||||
```
|
||||
|
||||
**Заглушки**:
|
||||
|
||||
* Соответствие Bronze/Silver/Gold.
|
||||
* Что происходит на каждом переходе в 1–2 фразы.
|
||||
|
||||
---
|
||||
|
||||
## 4. Слои по отдельности
|
||||
|
||||
### 4.1 STG (Staging/Bronze)
|
||||
|
||||
**Коротко**: «как пришло». Идемпотентность, дедупликация, неизменяемость/переигрузка.
|
||||
|
||||
**TODO**: список «что можно» / «что нельзя»; форматы; контроль качества на входе.
|
||||
|
||||
### 4.2 ODS (Operational Data Store/Silver)
|
||||
|
||||
**Коротко**: чистка и выравнивание типов, базовая унификация кодов, ещё без тяжёлой бизнес-логики.
|
||||
|
||||
**TODO**: правила именования, ключи, простая история.
|
||||
|
||||
### 4.3 DDS (Integrated/Conformed Layer)
|
||||
|
||||
**Коротко**: интеграция источников, общие справочники, SK/BK, SCD. Здесь уже начинаются модели данных. Какие модели испольуют.
|
||||
|
||||
**TODO**: где хранить историю, антидубли, конформные измерения.
|
||||
|
||||
### 4.4 DM (Data Marts/Gold)
|
||||
|
||||
**Коротко**: модели под задачи BI (звезда/снежинка). Агрегаты, материализации.
|
||||
|
||||
**TODO**: границы ответственности витрин.
|
||||
|
||||
---
|
||||
|
||||
## 5. Модели данных для DDS/DM: 3NF, Звезда/Снежинка, Data Vault, Anchor
|
||||
|
||||
### 5.1 3NF (по Инмону, 3-я нормальная форма)
|
||||
|
||||
**Идея**: целостная интегрированная модель предприятия.
|
||||
|
||||
**Мини-эскиз (ER, нормализовано)**:
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
ORDER ||--|{ ORDER_ITEM : contains
|
||||
PRODUCT ||--o{ ORDER_ITEM : referenced
|
||||
CATEGORY ||--o{ PRODUCT : classifies
|
||||
PRICE ||--o{ PRODUCT : defines
|
||||
```
|
||||
|
||||
**TODO**: плюсы/минусы, когда выбирать.
|
||||
|
||||
### 5.2 Звезда/Снежинка (по Кимбаллу)
|
||||
|
||||
**Идея**: простые и быстрые аналитические запросы.
|
||||
Упомянуть, что самая часто испольуемая модель.
|
||||
|
||||
**Эскиз звезды: факт + измерения**:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
F[fact_orders<br/>order_id, date_key, customer_key, product_key, qty, amount]
|
||||
D1[dim_date] --> F
|
||||
D2[dim_customer] --> F
|
||||
D3[dim_product] --> F
|
||||
D4[dim_store] --> F
|
||||
```
|
||||
|
||||
**Эскиз снежинки (нормализация измерения продукта)**:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
D3[dim_product] --> C[dim_category]
|
||||
```
|
||||
|
||||
**TODO**: зерно факта, типы измерений, агрегаты.
|
||||
|
||||
### 5.3 Data Vault 2.0 (Hub–Link–Satellite)
|
||||
|
||||
**Идея**: масштабируемая интеграция многоисточниковых данных с полной историей.
|
||||
|
||||
**Эскиз DV (упорядоченная колонками)**:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
%% Hubs (слева)
|
||||
HC[Hub_Customer]
|
||||
HO[Hub_Order]
|
||||
HP[Hub_Product]
|
||||
|
||||
%% Links (центр)
|
||||
LOC[Link_OrderCustomer]
|
||||
LOP[Link_OrderProduct]
|
||||
|
||||
%% Satellites (справа)
|
||||
SC[SAT_Customer]
|
||||
SO[SAT_Order]
|
||||
SP[SAT_Product]
|
||||
|
||||
%% Связи к сателлитам
|
||||
HC --> SC
|
||||
HO --> SO
|
||||
HP --> SP
|
||||
|
||||
%% Связи между хабами и линками
|
||||
HC --> LOC
|
||||
HO --> LOC
|
||||
HO --> LOP
|
||||
HP --> LOP
|
||||
```
|
||||
|
||||
**TODO**: Raw Vault vs Business Vault, плюс/минус.
|
||||
На каких слоях используем, зачем там оно нам.
|
||||
|
||||
Схема часто используется. Пишем, почему.
|
||||
|
||||
### 5.4 Anchor Modeling (Анкерное моделирование)
|
||||
|
||||
**Идея**: эволюционируемость атрибутов с версионированием на уровне «якорей/атрибутов/узлов».
|
||||
|
||||
**Эскиз (упрощённый)**:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
AC[Anchor CUSTOMER]
|
||||
KC[Knot CUSTOMER_ID]
|
||||
AC --> KC
|
||||
AC --> AN[Attr NAME]
|
||||
AC --> AE[Attr EMAIL]
|
||||
AN --> NV[Name values history]
|
||||
AE --> EV[Email values history]
|
||||
```
|
||||
|
||||
**TODO**: где уместно, порог входа.
|
||||
|
||||
**TODO**: где уместно, порог входа.
|
||||
|
||||
Редко используется. Сложна для людей и для машин. Обосновываем.
|
||||
|
||||
---
|
||||
|
||||
## 6. Ключевые понятия (минимум для практики)
|
||||
|
||||
* **Факты (Facts)** и **Измерения (Dimensions)**; **зерно (grain)** факта.
|
||||
* **Натуральные ключи (BK)** и **суррогатные ключи (SK)**.
|
||||
* **SCD (Slowly Changing Dimensions)**: типы 1 / 2 / 4 / 6.
|
||||
* **Bridge/Junk dimensions**, календарь, валюта, часовой пояс.
|
||||
* **Поздно прибывающие события (late arriving)**.
|
||||
|
||||
**Эскиз SCD‑истории (Type 2) как лента времени**:
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
title SCD Type 2: dim_customer.name
|
||||
section Customer 42
|
||||
Version_1 :active, v1, 2023-01-01, 2023-06-14
|
||||
Version_2 : v2, 2023-06-15, 2024-02-28
|
||||
Version_3 : v3, 2024-03-01, 2025-10-26
|
||||
```
|
||||
|
||||
**TODO**: короткие примеры для Type 1/2/4/6.
|
||||
|
||||
---
|
||||
|
||||
## 7. Эволюция схемы и эксплуатационные практики
|
||||
|
||||
* Совместимость назад/вперёд, view‑based миграции.
|
||||
* Идемпотентные пайплайны, дедупликация, инкрементальные загрузки (CDC).
|
||||
* Data Quality: уникальность, ссылочная целостность, распределения, «contracts».
|
||||
* Каталог/линейка (Data Catalog / Lineage), словарь данных (Business Glossary), владение.
|
||||
|
||||
**TODO**: чек-лист из 8–10 пунктов.
|
||||
|
||||
---
|
||||
## 10. Заключение
|
||||
|
||||
**Заглушки**: повторить ключевые тезисы, дать ссылки на дополнительные темы: CDC, оркестрация, тестирование данных, наблюдаемость.
|
||||
|
||||
---
|
||||
|
||||
## Примечания к иллюстрациям
|
||||
|
||||
* Диаграммы — **эскизы**: заменить/уточнить, когда будет готов текст.
|
||||
* При необходимости добавить отдельные рисунки: «что можно/нельзя в слоях», SCD типы на одном полотне, пример late arriving events.
|
||||
@@ -1,549 +0,0 @@
|
||||
Что нас здесь ждет
|
||||
|
||||
* **Уже умеете:** SQL-основы (SELECT/JOIN/GROUP BY), простые агрегаты.
|
||||
* **Узнаете:** слои (STG/ODS/DDS/DM), модели (3NF/Star/DV/Anchor), SCD и суррогатные ключи.
|
||||
* **Не рассматриваем здесь:** физический дизайн, производительность, партиционирование, распределённые кластеры — это отдельная тема.
|
||||
|
||||
# **Структура статьи про хранилище данных **
|
||||
|
||||
1. Введение. Проблема аналитики в OLTP
|
||||
2. Учебный пример. Интернет-магазин
|
||||
3. Архитектура. Зачем слои?
|
||||
4. Путешествие данных. STG→ODS→DDS→DM
|
||||
5. Базовые понятия. Факты, Измерения, SCD
|
||||
6. Модели данных. 3NF, DV, Звезда
|
||||
7. Практикум. Собираем витрину
|
||||
8. Выбор моделию Дерево решений
|
||||
9. Эксплуатация. Качество и эволюция
|
||||
10. Заключение
|
||||
|
||||
---
|
||||
|
||||
# **1. Введение: Аналитика — это не оперативный учет**
|
||||
|
||||
**OLTP (оперативный учёт)**: CRM, заказы, склад.
|
||||
**DWH (аналитическое хранилище)**: единая модель для анализа.
|
||||
|
||||
Проблема: в OLTP для исторической аналитики — сложные JOIN, тяжёлые агрегации, нестабильная производительность.
|
||||
|
||||
**Особенности DWH**:
|
||||
|
||||
* Слоистая архитектура
|
||||
* Оптимизированные под аналитику модели
|
||||
|
||||
**Ключевые тезисы**:
|
||||
|
||||
* OLTP vs OLAP: транзакции против анализа
|
||||
* Почему «одна большая таблица» не работает на истории
|
||||
* 3 преимущества слоёв: управляемость, производительность, прозрачность
|
||||
|
||||
---
|
||||
|
||||
# **2. Учебный пример: интернет-магазин**
|
||||
|
||||
ER-диаграмма сущностей источника (упрощённо):
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
ORDER ||--|{ ORDER_ITEM : contains
|
||||
PRODUCT ||--o{ ORDER_ITEM : referenced
|
||||
PRODUCT ||--o{ PRICE : has
|
||||
PROMO ||--o{ ORDER_ITEM : applied
|
||||
CUSTOMER {
|
||||
int customer_id PK
|
||||
string email
|
||||
string phone
|
||||
}
|
||||
PRODUCT {
|
||||
int product_id PK
|
||||
string name
|
||||
}
|
||||
ORDER {
|
||||
int order_id PK
|
||||
date order_date
|
||||
int customer_id FK
|
||||
}
|
||||
ORDER_ITEM {
|
||||
int order_item_id PK
|
||||
int order_id FK
|
||||
int product_id FK
|
||||
int qty
|
||||
numeric price_at_sale
|
||||
}
|
||||
PRICE {
|
||||
int product_id FK
|
||||
date valid_from
|
||||
date valid_to
|
||||
numeric price
|
||||
}
|
||||
PROMO {
|
||||
int promo_id PK
|
||||
string code
|
||||
}
|
||||
```
|
||||
|
||||
Описание примера текстом
|
||||
|
||||
---
|
||||
|
||||
# **3. Архитектура хранилища: зачем делить на слои?**
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Sources[Источники OLTP]
|
||||
A[CRM]
|
||||
B[Заказы]
|
||||
C[Склад]
|
||||
end
|
||||
|
||||
A --> STG
|
||||
B --> STG
|
||||
C --> STG
|
||||
|
||||
subgraph STG[STG - Сырые данные]
|
||||
D[Таблицы-клоны<br>сырые данные]
|
||||
end
|
||||
|
||||
STG --> ODS
|
||||
|
||||
subgraph ODS[ODS - Очищенные данные]
|
||||
E[Стандартизированные<br>типы и форматы]
|
||||
end
|
||||
|
||||
ODS --> DDS
|
||||
|
||||
subgraph DDS[DDS - Интегрированная модель]
|
||||
F[Бизнес-сущности<br>SCD, интеграция]
|
||||
end
|
||||
|
||||
DDS --> DM
|
||||
|
||||
subgraph DM[DM - Витрины для аналитики]
|
||||
G[Звезда/снежинка<br>агрегаты]
|
||||
end
|
||||
|
||||
DM --> BI[BI-системы<br>и отчеты]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **4. Путешествие данных по слоям**
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph STG[STG - Сырье]
|
||||
A[raw_orders.json]
|
||||
B[raw_customers.csv]
|
||||
C[Формат как в источнике]
|
||||
end
|
||||
|
||||
subgraph ODS[ODS - Очистка]
|
||||
D[stg_orders]
|
||||
E[stg_customers]
|
||||
F[Типизация, валидация]
|
||||
end
|
||||
|
||||
subgraph DDS[DDS - Интеграция]
|
||||
G[dim_customer<br>SCD Type 2]
|
||||
H[fact_orders<br>с суррогатными ключами]
|
||||
end
|
||||
|
||||
subgraph DM[DM - Готовые решения]
|
||||
I[mart_sales<br>агрегированные данные]
|
||||
J[mart_customer_360<br>обзор по клиентам]
|
||||
end
|
||||
|
||||
STG --> ODS
|
||||
ODS --> DDS
|
||||
DDS --> DM
|
||||
```
|
||||
|
||||
STG (Staging/Bronze)
|
||||
|
||||
Коротко: «как пришло». Идемпотентность, дедупликация, неизменяемость/переигрузка.
|
||||
|
||||
ODS (Operational Data Store/Silver)
|
||||
|
||||
Коротко: чистка и выравнивание типов, базовая унификация кодов, ещё без тяжёлой бизнес-логики.
|
||||
|
||||
DDS (Integrated/Conformed)
|
||||
|
||||
Коротко: интеграция источников, общие справочники, SK/BK, SCD. Здесь живут модели данных (3NF/DV/Anchor/Star).
|
||||
|
||||
DM (Data Marts/Gold)
|
||||
|
||||
Коротко: модели под задачи BI (звезда/снежинка). Агрегаты, материализации.
|
||||
|
||||
* **STG (Bronze):** как пришло; неизменяемо/переигрузка; идемпотентность; дедуп по `(BK, load_ts)`; только тех. обогащения (например, `_ingest_id`).
|
||||
* **ODS (Silver):** типизация; базовая очистка и стандартные коды; **без** тяжёлой бизнес-логики; стабильные схемы/имена.
|
||||
* **DDS (Conformed/Core):** интеграция источников; единая терминология; SK/BK; SCD; здесь живут модели (3NF/Star/DV/Anchor).
|
||||
* **DM (Gold):** под конкретные вопросы BI/продукта; агрегаты/материализации; доступные метрики и измерения.
|
||||
|
||||
Нейминг-гайд (пример)
|
||||
|
||||
* `stg.*` — как в источнике, добавочные тех. поля: `_ingest_id`, `_load_ts`.
|
||||
* `ods.*` — очищенные «плоские» таблицы, стаб. схемы.
|
||||
* `dds.dim_*`, `dds.fact_*` — интегрированная модель.
|
||||
* `dm.mart_*` — витрины/представления.
|
||||
|
||||
---
|
||||
|
||||
## **5. Базовые понятия: Факты, Измерения, Ключи**
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
dim_date ||--o{ fact_sales : "дата"
|
||||
dim_customer ||--o{ fact_sales : "клиент"
|
||||
dim_product ||--o{ fact_sales : "товар"
|
||||
|
||||
dim_customer {
|
||||
bigint customer_sk PK
|
||||
varchar customer_bk
|
||||
varchar customer_name
|
||||
varchar email
|
||||
date valid_from
|
||||
date valid_to
|
||||
boolean is_current
|
||||
}
|
||||
|
||||
fact_sales {
|
||||
bigint sale_id PK
|
||||
bigint customer_sk FK
|
||||
bigint product_sk FK
|
||||
date date_sk FK
|
||||
int quantity
|
||||
decimal amount
|
||||
}
|
||||
```
|
||||
|
||||
Что из базвой модели данных
|
||||
|
||||
**SCD Type 2 - Визуализация истории**:
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title SCD Type 2: История изменений клиента (ID = 123)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %Y-%m
|
||||
|
||||
section Москва, premium@email.com
|
||||
Версия 1 :active, 2023-01-01, 2023-05-15
|
||||
|
||||
section Москва, new_premium@email.com
|
||||
Версия 2 :active, 2023-05-16, 2023-09-30
|
||||
|
||||
section Санкт-Петербург, new_premium@email.com
|
||||
Версия 3 :active, 2023-10-01, 2024-12-31
|
||||
```
|
||||
в fact берём версию dim_customer по дате факта (между valid_from и valid_to)
|
||||
|
||||
Краткое описание текстом. Указание, что подробно про scd можно почитать в соседней статье SCD.md
|
||||
|
||||
---
|
||||
|
||||
## **6. Модели данных для DDS**
|
||||
|
||||
### 3NF (третья нормальная форма)
|
||||
|
||||
Классическая нормализованная модель, сильная целостность и интеграция, но SQL для аналитики сложнее.
|
||||
|
||||
### Снежинка (Snowflake)
|
||||
|
||||
Нормализованные измерения поверх звезды — компромисс между читаемостью и дублированием.
|
||||
|
||||
### Звезда (Star Schema)
|
||||
|
||||
Факт + денормализованные измерения — просто и быстро для BI/SQL.
|
||||
|
||||
### **Data Vault 2.0**:
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
hub_customer ||--o{ sat_customer_info : "хаб"
|
||||
hub_order ||--o{ sat_order_details : "хаб"
|
||||
hub_product ||--o{ sat_product_info : "хаб"
|
||||
|
||||
hub_customer ||--o{ link_order_customer : "участвует"
|
||||
hub_order ||--o{ link_order_customer : "включает"
|
||||
|
||||
hub_order ||--o{ link_order_product : "содержит"
|
||||
hub_product ||--o{ link_order_product : "входит в"
|
||||
|
||||
hub_customer {
|
||||
string customer_hash_key PK
|
||||
string customer_id BK
|
||||
datetime load_dttm
|
||||
}
|
||||
|
||||
sat_customer_info {
|
||||
string customer_hash_key PK,FK
|
||||
datetime load_dttm PK
|
||||
string customer_name
|
||||
string email
|
||||
string phone
|
||||
}
|
||||
|
||||
link_order_customer {
|
||||
string order_customer_hash_key PK
|
||||
string order_hash_key FK
|
||||
string customer_hash_key FK
|
||||
datetime load_dttm
|
||||
}
|
||||
```
|
||||
|
||||
Коротко: хабы (BK), линки (связи), сателлиты (история атрибутов). Этот раздел обзорный.
|
||||
|
||||
Упомянуть про DV 1
|
||||
|
||||
### Anchor Modeling (анкерное моделирование)
|
||||
|
||||
Атомарная декомпозиция сущностей и атрибутов, гибкая эволюция схемы; высокая гранулярность усложняет чтение.
|
||||
|
||||
> **Сравнение (интуитивно):** Звезда — проще/быстрее для BI; 3NF — целостность и интеграция; DV/Anchor — масштабируемая интеграция из многих источников и «история по умолчанию», но сложнее читать и писать.
|
||||
|
||||
**Сравнение моделей**:
|
||||
|
||||
```mermaid
|
||||
quadrantChart
|
||||
title Сравнение моделей данных по сложности и гибкости
|
||||
x-axis "Низкая сложность" --> "Высокая сложность"
|
||||
y-axis "Низкая гибкость" --> "Высокая гибкость"
|
||||
"Звезда": [0.2, 0.3]
|
||||
"3NF": [0.6, 0.5]
|
||||
"Data Vault": [0.8, 0.8]
|
||||
"Anchor": [0.9, 0.9]
|
||||
```
|
||||
Рассказать, что выбор модели для данных далеко не всегда однозначен, и является компромисснным.
|
||||
|
||||
3NF - преимущества и недостатки
|
||||
Снежинка - преимущества и недостатки
|
||||
DV
|
||||
Anchor Modeling
|
||||
|
||||
---
|
||||
|
||||
#### **7. Практикум: собираем витрину**
|
||||
|
||||
Раздел мне не нравится, надо бы переработать.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Источники] --> STG
|
||||
STG[STG: сырые заказы, товары] --> ODS
|
||||
ODS[ODS: очищенные данные] --> DDS
|
||||
|
||||
subgraph DDS[DDS: интегрированная модель]
|
||||
B[dim_customer<br>SCD Type 2]
|
||||
C[dim_product]
|
||||
D[dim_date]
|
||||
E[fact_sales]
|
||||
end
|
||||
|
||||
DDS --> F{Сборка витрины}
|
||||
|
||||
F --> G[mart_daily_sales]
|
||||
F --> H[mart_customer_lifetime]
|
||||
|
||||
G --> I[Дашборд продаж]
|
||||
H --> J[Отчет по клиентам]
|
||||
```
|
||||
|
||||
**Пример SQL для витрины**:
|
||||
```sql
|
||||
-- Витрина ежедневных продаж
|
||||
CREATE TABLE mart_daily_sales AS
|
||||
SELECT
|
||||
d.date,
|
||||
p.product_name,
|
||||
c.customer_segment,
|
||||
SUM(f.quantity) as total_quantity,
|
||||
SUM(f.amount) as total_amount
|
||||
FROM fact_sales f
|
||||
JOIN dim_date d ON f.date_key = d.date_key
|
||||
JOIN dim_product p ON f.product_key = p.product_key
|
||||
JOIN dim_customer c ON f.customer_key = c.customer_key
|
||||
WHERE c.is_current = true
|
||||
GROUP BY d.date, p.product_name, c.customer_segment;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **9. Эксплуатация: качество и эволюция**
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
A[Данные] --> B{Контроль качества}
|
||||
|
||||
B --> C[Проверка уникальности]
|
||||
B --> D[Проверка полноты]
|
||||
B --> E[Валидация форматов]
|
||||
B --> F[Свежесть данных]
|
||||
|
||||
C --> G[✅ Успех]
|
||||
D --> G
|
||||
E --> G
|
||||
F --> G
|
||||
|
||||
C --> H[❌ Ошибка]
|
||||
D --> H
|
||||
E --> H
|
||||
F --> H
|
||||
|
||||
G --> I[Загрузка в слой]
|
||||
H --> J[Оповещение<br>и остановка]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### **10. Заключение: главное — понимать "почему"**
|
||||
|
||||
```mermaid
|
||||
mindmap
|
||||
root((Хранилище данных))
|
||||
Архитектура
|
||||
Слои STG→ODS→DDS→DM
|
||||
Разделение ответственности
|
||||
Управляемость изменений
|
||||
Модели данных
|
||||
3NF: Целостность
|
||||
Data Vault: Масштабируемость
|
||||
Звезда: Производительность
|
||||
Ключевые понятия
|
||||
Факты и измерения
|
||||
Суррогатные ключи
|
||||
SCD Type 2
|
||||
Практика
|
||||
Понимать бизнес-задачу
|
||||
Выбирать подходящую модель
|
||||
Строить итеративно
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **10. Заключение: главное — понимать «почему»**
|
||||
|
||||
Ключевые идеи: разделение на слои (STG → ODS → DDS → DM), разные модели для разных задач (3NF, Star, DV, Anchor), факты/измерения/SCD, итеративная сборка витрин под конкретные вопросы бизнеса.
|
||||
|
||||
# Приложения
|
||||
|
||||
### 2) Мини-глоссарий RU/EN (по 1–2 строки)
|
||||
|
||||
* **Слой (Layer)** — логический уровень в DWH: STG/ODS/DDS/DM.
|
||||
* **Витрина (Data Mart)** — предметно-ориентированный набор таблиц/представлений для конкретной аналитики.
|
||||
* **Факт (Fact)** — таблица событий/измерений величин (кол-во, сумма).
|
||||
* **Измерение (Dimension)** — справочник контекста фактов (клиенты, товары, даты).
|
||||
* **Суррогатный ключ (Surrogate Key, SK)** — искусственный технический ключ (int/bigint).
|
||||
* **Бизнес-ключ (Business Key, BK)** — естественный ключ из источника (например, `customer_id`).
|
||||
* **SCD (Slowly Changing Dimension)** — подход к хранению истории атрибутов измерения.
|
||||
* **CDC (Change Data Capture)** — техника инкрементальной загрузки изменений.
|
||||
* **Conformed Dimension** — «конформное» измерение, общее для нескольких витрин.
|
||||
|
||||
### 3) Отображение синонимов слоёв
|
||||
|
||||
* **STG (Bronze)** → **ODS (Silver)** → **DDS (Conformed/Core)** → **DM (Gold)**.
|
||||
|
||||
### 4) «Правила слоя» — короткие чек-листы
|
||||
|
||||
|
||||
|
||||
### 5) Антипаттерны (короткий бокс)
|
||||
|
||||
* «Одна огромная историческая таблица» → медленные запросы, нет истории атрибутов.
|
||||
* «Смешали STG и ODS» → потеря трассировки ошибок и инцидентов качества.
|
||||
* «Факт с текстовыми атрибутами без причин» → раздутая таблица и неявные бизнес-правила.
|
||||
* «SCD без BK» → история «плывёт», не привязана к бизнес-идентификатору.
|
||||
|
||||
|
||||
|
||||
|
||||
### 10) «Зачем разные модели» (без выбора)
|
||||
|
||||
* **Star (звезда):** быстро писать отчёты, учим новичков на ней.
|
||||
* **3NF:** лучшая консистентность/интеграция понятий, тяжелее для BI.
|
||||
* **Data Vault:** масштабируемая интеграция множества источников + «история по умолчанию».
|
||||
* **Anchor:** гибкая эволюция схемы, максимальная атомарность, цена — сложность.
|
||||
|
||||
### 11) Что почитать дальше
|
||||
|
||||
* **Kimball, The Data Warehouse Toolkit** — модели «звезды», SCD.
|
||||
* **Inmon, Building the Data Warehouse** — корпоративный DWH и нормализация.
|
||||
* **Linstedt & Olschimke, The Data Vault 2.0** — практический DV.
|
||||
* **Anchor Modeling** — официальный сайт/документация по Anchor.
|
||||
|
||||
---
|
||||
|
||||
## ЧАСТЬ C. Мини-датасет (для примеров в статье)
|
||||
|
||||
> Опционально приложить к репозиторию/приложению статьи.
|
||||
|
||||
**customers.csv**
|
||||
|
||||
```
|
||||
customer_id,email,phone,city
|
||||
101,a@ex.com,700,Москва
|
||||
101,b@ex.com,700,Москва
|
||||
102,c@ex.com,701,СПб
|
||||
```
|
||||
|
||||
**orders.csv**
|
||||
|
||||
```
|
||||
order_id,order_date,customer_id
|
||||
5001,2024-01-10,101
|
||||
5002,2024-02-05,102
|
||||
```
|
||||
|
||||
**order_items.csv**
|
||||
|
||||
```
|
||||
order_item_id,order_id,product_id,qty,price_at_sale
|
||||
1,5001,9001,2,100.00
|
||||
2,5001,9002,1,50.00
|
||||
3,5002,9001,1,100.00
|
||||
```
|
||||
|
||||
**products.csv**
|
||||
|
||||
```
|
||||
product_id,name
|
||||
9001,Phone
|
||||
9002,Case
|
||||
```
|
||||
|
||||
**prices.csv**
|
||||
|
||||
```
|
||||
product_id,valid_from,valid_to,price
|
||||
9001,2023-12-01,2024-01-31,100
|
||||
9001,2024-02-01,2999-12-31,110
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ЧАСТЬ D. DDL-скелеты (минимально)
|
||||
|
||||
```sql
|
||||
-- DDS: измерение клиента (Type 2)
|
||||
CREATE TABLE dds.dim_customer (
|
||||
customer_sk BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
customer_bk VARCHAR NOT NULL,
|
||||
email VARCHAR,
|
||||
phone VARCHAR,
|
||||
city VARCHAR,
|
||||
valid_from DATE NOT NULL,
|
||||
valid_to DATE,
|
||||
is_current BOOLEAN NOT NULL DEFAULT TRUE
|
||||
);
|
||||
|
||||
-- DDS: факт продаж (гранулярность: строка заказа)
|
||||
CREATE TABLE dds.fact_sales (
|
||||
sale_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
customer_sk BIGINT NOT NULL,
|
||||
product_sk BIGINT NOT NULL,
|
||||
date_sk INT NOT NULL,
|
||||
quantity INT NOT NULL,
|
||||
amount DECIMAL(18,2) NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
@@ -1,570 +0,0 @@
|
||||
# Модели данных и слои DWH (для менти)
|
||||
|
||||
**Цель и аудитория:** эта статья для начинающих аналитиков/инженеров данных, которые уже умеют базовый SQL (SELECT/JOIN/GROUP BY/WHERE) и переходят к моделированию данных и архитектуре хранилища (Data Warehouse, DWH). Здесь даём общее представление: **зачем слои**, **что такое модели**, **чем они отличаются**, где обычно живут **факты/измерения/SCD**. **Выбор модели** в продакшен за вас делать не будем — на этом этапе важно понять, *зачем разные модели существуют* и *какие задачи они решают*.
|
||||
|
||||
---
|
||||
|
||||
## ЧАСТЬ A. Исходный черновик статьи (переделан в чистовик с лёгкой правкой опечаток)
|
||||
|
||||
Ниже — аккуратно отредактированная версия вашего плана с диаграммами Mermaid. Сохранили исходную структуру и формулировки, поправили опечатки и выровняли стиль именования.
|
||||
|
||||
---
|
||||
|
||||
# **Структура статьи про хранилище данных**
|
||||
|
||||
1. Введение. Проблема аналитики в OLTP
|
||||
2. Учебный пример. Интернет-магазин
|
||||
3. Архитектура. Зачем слои?
|
||||
4. Путешествие данных. STG → ODS → DDS → DM
|
||||
5. Базовые понятия. Факты, Измерения, SCD
|
||||
6. Модели данных. 3NF, Data Vault, Звезда
|
||||
7. Практикум. Собираем витрину
|
||||
8. Зачем разные модели (демо-«дерево решений»)
|
||||
9. Эксплуатация. Качество и эволюция
|
||||
10. Заключение
|
||||
|
||||
> Примечание про Mermaid: диаграммы `mindmap` и `quadrantChart` поддерживаются не всеми рендерами. Если планируете публикацию на площадке со строгим Markdown (например, Habr), оставьте текстовую альтернативу.
|
||||
|
||||
---
|
||||
|
||||
## **1. Введение: аналитика — это не оперативный учёт**
|
||||
|
||||
**OLTP (оперативный учёт)**: CRM, заказы, склад.
|
||||
**DWH (аналитическое хранилище)**: единая модель для анализа.
|
||||
|
||||
Проблема: в OLTP для исторической аналитики — сложные JOIN, тяжёлые агрегации, нестабильная производительность.
|
||||
|
||||
**Особенности DWH**:
|
||||
|
||||
* Слоистая архитектура
|
||||
* Оптимизированные под аналитику модели
|
||||
|
||||
**Ключевые тезисы**:
|
||||
|
||||
* OLTP vs OLAP: транзакции против анализа
|
||||
* Почему «одна большая таблица» не работает на истории
|
||||
* 3 преимущества слоёв: управляемость, производительность, прозрачность
|
||||
|
||||
---
|
||||
|
||||
## **2. Учебный пример: интернет-магазин**
|
||||
|
||||
ER-диаграмма сущностей источника (упрощённо):
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
CUSTOMER ||--o{ ORDER : places
|
||||
ORDER ||--|{ ORDER_ITEM : contains
|
||||
PRODUCT ||--o{ ORDER_ITEM : referenced
|
||||
PRODUCT ||--o{ PRICE : has
|
||||
PROMO ||--o{ ORDER_ITEM : applied
|
||||
CUSTOMER {
|
||||
int customer_id PK
|
||||
string email
|
||||
string phone
|
||||
}
|
||||
PRODUCT {
|
||||
int product_id PK
|
||||
string name
|
||||
}
|
||||
ORDER {
|
||||
int order_id PK
|
||||
date order_date
|
||||
int customer_id FK
|
||||
}
|
||||
ORDER_ITEM {
|
||||
int order_item_id PK
|
||||
int order_id FK
|
||||
int product_id FK
|
||||
int qty
|
||||
numeric price_at_sale
|
||||
}
|
||||
PRICE {
|
||||
int product_id FK
|
||||
date valid_from
|
||||
date valid_to
|
||||
numeric price
|
||||
}
|
||||
PROMO {
|
||||
int promo_id PK
|
||||
string code
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **3. Архитектура хранилища: зачем делить на слои?**
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Sources[Источники OLTP]
|
||||
A[CRM]
|
||||
B[Заказы]
|
||||
C[Склад]
|
||||
end
|
||||
|
||||
A --> STG
|
||||
B --> STG
|
||||
C --> STG
|
||||
|
||||
subgraph STG[STG (Bronze) — сырые данные]
|
||||
D[Таблицы-клоны \n неизменяемые/переигрузка]
|
||||
end
|
||||
|
||||
STG --> ODS
|
||||
|
||||
subgraph ODS[ODS (Silver) — очищенные]
|
||||
E[Типизация, валидация, \n стандартизация кодов]
|
||||
end
|
||||
|
||||
ODS --> DDS
|
||||
|
||||
subgraph DDS[DDS — интегрированная модель]
|
||||
F[Бизнес-сущности, SK/BK, SCD]
|
||||
end
|
||||
|
||||
DDS --> DM
|
||||
|
||||
subgraph DM[DM (Gold) — витрины]
|
||||
G[Звезда/Снежинка, агрегаты]
|
||||
end
|
||||
|
||||
DM --> BI[BI/Отчёты]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **4. Путешествие данных по слоям**
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph STG[STG — сырьё]
|
||||
A[raw_orders.json]
|
||||
B[raw_customers.csv]
|
||||
C[Формат как в источнике]
|
||||
end
|
||||
|
||||
subgraph ODS[ODS — очистка]
|
||||
D[stg_orders]
|
||||
E[stg_customers]
|
||||
F[Типизация, валидация]
|
||||
end
|
||||
|
||||
subgraph DDS[DDS — интеграция]
|
||||
G[dim_customer — SCD2]
|
||||
H[fact_orders — с суррогатными ключами]
|
||||
end
|
||||
|
||||
subgraph DM[DM — готовые решения]
|
||||
I[mart_sales — агрегаты]
|
||||
J[mart_customer_360]
|
||||
end
|
||||
|
||||
STG --> ODS
|
||||
ODS --> DDS
|
||||
DDS --> DM
|
||||
```
|
||||
|
||||
### 4.1 STG (Staging/Bronze)
|
||||
|
||||
Коротко: «как пришло». Идемпотентность, дедупликация, неизменяемость/переигрузка.
|
||||
|
||||
### 4.2 ODS (Operational Data Store/Silver)
|
||||
|
||||
Коротко: чистка и выравнивание типов, базовая унификация кодов, ещё без тяжёлой бизнес-логики.
|
||||
|
||||
### 4.3 DDS (Integrated/Conformed)
|
||||
|
||||
Коротко: интеграция источников, общие справочники, SK/BK, SCD. Здесь живут модели данных (3NF/DV/Anchor/Star).
|
||||
|
||||
### 4.4 DM (Data Marts/Gold)
|
||||
|
||||
Коротко: модели под задачи BI (звезда/снежинка). Агрегаты, материализации.
|
||||
|
||||
---
|
||||
|
||||
## **5. Базовые понятия: факты, измерения, ключи**
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
dim_date ||--o{ fact_sales : "дата"
|
||||
dim_customer ||--o{ fact_sales : "клиент"
|
||||
dim_product ||--o{ fact_sales : "товар"
|
||||
|
||||
dim_customer {
|
||||
bigint customer_sk PK
|
||||
varchar customer_bk
|
||||
varchar customer_name
|
||||
varchar email
|
||||
date valid_from
|
||||
date valid_to
|
||||
boolean is_current
|
||||
}
|
||||
|
||||
fact_sales {
|
||||
bigint sale_id PK
|
||||
bigint customer_sk FK
|
||||
bigint product_sk FK
|
||||
int date_sk FK
|
||||
int quantity
|
||||
decimal amount
|
||||
}
|
||||
```
|
||||
|
||||
**SCD Type 2 — визуализация истории:**
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title SCD Type 2: История изменений клиента (ID = 123)
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %Y-%m
|
||||
|
||||
section Москва, premium@email.com
|
||||
Версия 1 :active, 2023-01-01, 2023-05-15
|
||||
|
||||
section Москва, new_premium@email.com
|
||||
Версия 2 :active, 2023-05-16, 2023-09-30
|
||||
|
||||
section Санкт-Петербург, new_premium@email.com
|
||||
Версия 3 :active, 2023-10-01, 2024-12-31
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **6. Модели данных для DDS**
|
||||
|
||||
### 3NF (третья нормальная форма)
|
||||
|
||||
Классическая нормализованная модель, сильная целостность и интеграция, но SQL для аналитики сложнее.
|
||||
|
||||
### Снежинка (Snowflake)
|
||||
|
||||
Нормализованные измерения поверх звезды — компромисс между читаемостью и дублированием.
|
||||
|
||||
### Звезда (Star Schema)
|
||||
|
||||
Факт + денормализованные измерения — просто и быстро для BI/SQL.
|
||||
|
||||
### **Data Vault 2.0**
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
HUB_CUSTOMER ||--o{ SAT_CUSTOMER : has_history
|
||||
HUB_ORDER ||--o{ SAT_ORDER : has_history
|
||||
HUB_PRODUCT ||--o{ SAT_PRODUCT : has_history
|
||||
|
||||
HUB_CUSTOMER ||--o{ LNK_ORDER_CUSTOMER : participates
|
||||
HUB_ORDER ||--o{ LNK_ORDER_CUSTOMER : includes
|
||||
|
||||
HUB_ORDER ||--o{ LNK_ORDER_PRODUCT : contains
|
||||
HUB_PRODUCT ||--o{ LNK_ORDER_PRODUCT : involved
|
||||
|
||||
HUB_CUSTOMER {
|
||||
string customer_hk PK
|
||||
string customer_bk
|
||||
datetime load_dts
|
||||
string src
|
||||
}
|
||||
|
||||
LNK_ORDER_PRODUCT {
|
||||
string order_product_hk PK
|
||||
string order_hk FK
|
||||
string product_hk FK
|
||||
datetime load_dts
|
||||
string src
|
||||
}
|
||||
|
||||
SAT_CUSTOMER {
|
||||
string customer_hk PK,FK
|
||||
datetime load_dts PK
|
||||
string email
|
||||
string phone
|
||||
string city
|
||||
hashdiff hash
|
||||
}
|
||||
```
|
||||
|
||||
Коротко: хабы (BK), линки (связи), сателлиты (история атрибутов). Этот раздел обзорный.
|
||||
|
||||
### Anchor Modeling (анкерное моделирование)
|
||||
|
||||
Атомарная декомпозиция сущностей и атрибутов, гибкая эволюция схемы; высокая гранулярность усложняет чтение.
|
||||
|
||||
> **Сравнение (интуитивно):** Звезда — проще/быстрее для BI; 3NF — целостность и интеграция; DV/Anchor — масштабируемая интеграция из многих источников и «история по умолчанию», но сложнее читать и писать.
|
||||
|
||||
---
|
||||
|
||||
## **7. Практикум: собираем витрину**
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Источники] --> STG
|
||||
STG[STG: сырые заказы, товары] --> ODS
|
||||
ODS[ODS: очищенные данные] --> DDS
|
||||
|
||||
subgraph DDS[DDS: интегрированная модель]
|
||||
B[dim_customer (SCD2)]
|
||||
C[dim_product]
|
||||
D[dim_date]
|
||||
E[fact_sales]
|
||||
end
|
||||
|
||||
DDS --> F{Сборка витрины}
|
||||
|
||||
F --> G[mart_daily_sales]
|
||||
F --> H[mart_customer_lifetime]
|
||||
|
||||
G --> I[Дашборд продаж]
|
||||
H --> J[Отчёт по клиентам]
|
||||
```
|
||||
|
||||
**Пример SQL для витрины:**
|
||||
|
||||
```sql
|
||||
-- Витрина ежедневных продаж (пример)
|
||||
CREATE TABLE mart_daily_sales AS
|
||||
SELECT
|
||||
d.date,
|
||||
p.product_name,
|
||||
c.customer_segment,
|
||||
SUM(f.quantity) AS total_quantity,
|
||||
SUM(f.amount) AS total_amount
|
||||
FROM fact_sales f
|
||||
JOIN dim_date d ON f.date_sk = d.date_sk
|
||||
JOIN dim_product p ON f.product_sk = p.product_sk
|
||||
JOIN dim_customer c ON f.customer_sk = c.customer_sk AND c.is_current = TRUE
|
||||
GROUP BY d.date, p.product_name, c.customer_segment;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **9. Эксплуатация: качество и эволюция**
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
A[Данные] --> B{Контроль качества}
|
||||
B --> C[Проверка уникальности]
|
||||
B --> D[Проверка полноты]
|
||||
B --> E[Валидация форматов]
|
||||
B --> F[Свежесть данных]
|
||||
C --> G[✅ Успех]
|
||||
D --> G
|
||||
E --> G
|
||||
F --> G
|
||||
C --> H[❌ Ошибка]
|
||||
D --> H
|
||||
E --> H
|
||||
F --> H
|
||||
G --> I[Загрузка в слой]
|
||||
H --> J[Оповещение и остановка]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## **10. Заключение: главное — понимать «почему»**
|
||||
|
||||
Ключевые идеи: разделение на слои (STG → ODS → DDS → DM), разные модели для разных задач (3NF, Star, DV, Anchor), факты/измерения/SCD, итеративная сборка витрин под конкретные вопросы бизнеса.
|
||||
|
||||
---
|
||||
|
||||
## ЧАСТЬ B. Наши предложения к статье (что усилено и что добавить)
|
||||
|
||||
Ниже — блоки, которые можно встроить в соответствующие разделы или вынести в отдельные боксы/врезки.
|
||||
|
||||
### 1) Рамочный ввод «что вы знаете / что узнаете / чего не будет»
|
||||
|
||||
* **Уже умеете:** SQL-основы (SELECT/JOIN/GROUP BY), простые агрегаты.
|
||||
* **Узнаете:** слои (STG/ODS/DDS/DM), модели (3NF/Star/DV/Anchor), SCD и суррогатные ключи.
|
||||
* **Не рассматриваем здесь:** физический дизайн, производительность, партиционирование, распределённые кластеры — это отдельная тема.
|
||||
|
||||
### 2) Мини-глоссарий RU/EN (по 1–2 строки)
|
||||
|
||||
* **Слой (Layer)** — логический уровень в DWH: STG/ODS/DDS/DM.
|
||||
* **Витрина (Data Mart)** — предметно-ориентированный набор таблиц/представлений для конкретной аналитики.
|
||||
* **Факт (Fact)** — таблица событий/измерений величин (кол-во, сумма).
|
||||
* **Измерение (Dimension)** — справочник контекста фактов (клиенты, товары, даты).
|
||||
* **Суррогатный ключ (Surrogate Key, SK)** — искусственный технический ключ (int/bigint).
|
||||
* **Бизнес-ключ (Business Key, BK)** — естественный ключ из источника (например, `customer_id`).
|
||||
* **SCD (Slowly Changing Dimension)** — подход к хранению истории атрибутов измерения.
|
||||
* **CDC (Change Data Capture)** — техника инкрементальной загрузки изменений.
|
||||
* **Conformed Dimension** — «конформное» измерение, общее для нескольких витрин.
|
||||
|
||||
### 3) Отображение синонимов слоёв
|
||||
|
||||
* **STG (Bronze)** → **ODS (Silver)** → **DDS (Conformed/Core)** → **DM (Gold)**.
|
||||
|
||||
### 4) «Правила слоя» — короткие чек-листы
|
||||
|
||||
* **STG (Bronze):** как пришло; неизменяемо/переигрузка; идемпотентность; дедуп по `(BK, load_ts)`; только тех. обогащения (например, `_ingest_id`).
|
||||
* **ODS (Silver):** типизация; базовая очистка и стандартные коды; **без** тяжёлой бизнес-логики; стабильные схемы/имена.
|
||||
* **DDS (Conformed/Core):** интеграция источников; единая терминология; SK/BK; SCD; здесь живут модели (3NF/Star/DV/Anchor).
|
||||
* **DM (Gold):** под конкретные вопросы BI/продукта; агрегаты/материализации; доступные метрики и измерения.
|
||||
|
||||
### 5) Антипаттерны (короткий бокс)
|
||||
|
||||
* «Одна огромная историческая таблица» → медленные запросы, нет истории атрибутов.
|
||||
* «Смешали STG и ODS» → потеря трассировки ошибок и инцидентов качества.
|
||||
* «Факт с текстовыми атрибутами без причин» → раздутая таблица и неявные бизнес-правила.
|
||||
* «SCD без BK» → история «плывёт», не привязана к бизнес-идентификатору.
|
||||
|
||||
### 6) SCD2: рецепт джойна «по интервалу» (шпаргалка)
|
||||
|
||||
```sql
|
||||
-- Привязка факта к актуальной версии измерения (Type 2)
|
||||
SELECT f.*, d.customer_sk
|
||||
FROM fact_sales f
|
||||
JOIN dim_customer d
|
||||
ON d.customer_bk = f.customer_bk -- совпадение BK
|
||||
AND f.event_date >= d.valid_from
|
||||
AND f.event_date < COALESCE(d.valid_to, DATE '2999-12-31');
|
||||
```
|
||||
|
||||
### 7) Мини-практикум (сквозной, короткий)
|
||||
|
||||
1. Показать джойн факта к SCD2-измерению (по примеру выше).
|
||||
2. Собрать `mart_daily_sales` (у вас уже есть SQL).
|
||||
3. Пара проверок качества (см. ниже).
|
||||
|
||||
### 8) Качество данных: минимальные метрики + однострочники
|
||||
|
||||
* **Freshness (свежесть):** `MAX(event_time)` против «сейчас».
|
||||
* **Completeness (полнота):** доля NULL по важным столбцам.
|
||||
* **Consistency (согласованность):** факт без соответствия в измерении (нарушение покрытии ссылок).
|
||||
* **Uniqueness (уникальность):** уникальность BK в справочнике.
|
||||
|
||||
```sql
|
||||
-- Примеры «однострочников»
|
||||
-- Freshness
|
||||
SELECT NOW() - MAX(event_time) AS lag FROM ods.order;
|
||||
|
||||
-- Uniqueness BK
|
||||
SELECT customer_bk, COUNT(*)
|
||||
FROM dds.dim_customer
|
||||
GROUP BY customer_bk
|
||||
HAVING COUNT(*) > 1;
|
||||
|
||||
-- Orphan facts (без покрытия измерения)
|
||||
SELECT COUNT(*)
|
||||
FROM dds.fact_sales f
|
||||
LEFT JOIN dds.dim_customer d ON f.customer_sk = d.customer_sk
|
||||
WHERE d.customer_sk IS NULL;
|
||||
```
|
||||
|
||||
### 9) Нейминг-гайд (пример)
|
||||
|
||||
* `stg.*` — как в источнике, добавочные тех. поля: `_ingest_id`, `_load_ts`.
|
||||
* `ods.*` — очищенные «плоские» таблицы, стаб. схемы.
|
||||
* `dds.dim_*`, `dds.fact_*` — интегрированная модель.
|
||||
* `dm.mart_*` — витрины/представления.
|
||||
|
||||
### 10) «Зачем разные модели» (без выбора)
|
||||
|
||||
* **Star (звезда):** быстро писать отчёты, учим новичков на ней.
|
||||
* **3NF:** лучшая консистентность/интеграция понятий, тяжелее для BI.
|
||||
* **Data Vault:** масштабируемая интеграция множества источников + «история по умолчанию».
|
||||
* **Anchor:** гибкая эволюция схемы, максимальная атомарность, цена — сложность.
|
||||
|
||||
### 11) Что почитать дальше
|
||||
|
||||
* **Kimball, The Data Warehouse Toolkit** — модели «звезды», SCD.
|
||||
* **Inmon, Building the Data Warehouse** — корпоративный DWH и нормализация.
|
||||
* **Linstedt & Olschimke, The Data Vault 2.0** — практический DV.
|
||||
* **Anchor Modeling** — официальный сайт/документация по Anchor.
|
||||
|
||||
---
|
||||
|
||||
## ЧАСТЬ C. Мини-датасет (для примеров в статье)
|
||||
|
||||
> Опционально приложить к репозиторию/приложению статьи.
|
||||
|
||||
**customers.csv**
|
||||
|
||||
```
|
||||
customer_id,email,phone,city
|
||||
101,a@ex.com,700,Москва
|
||||
101,b@ex.com,700,Москва
|
||||
102,c@ex.com,701,СПб
|
||||
```
|
||||
|
||||
**orders.csv**
|
||||
|
||||
```
|
||||
order_id,order_date,customer_id
|
||||
5001,2024-01-10,101
|
||||
5002,2024-02-05,102
|
||||
```
|
||||
|
||||
**order_items.csv**
|
||||
|
||||
```
|
||||
order_item_id,order_id,product_id,qty,price_at_sale
|
||||
1,5001,9001,2,100.00
|
||||
2,5001,9002,1,50.00
|
||||
3,5002,9001,1,100.00
|
||||
```
|
||||
|
||||
**products.csv**
|
||||
|
||||
```
|
||||
product_id,name
|
||||
9001,Phone
|
||||
9002,Case
|
||||
```
|
||||
|
||||
**prices.csv**
|
||||
|
||||
```
|
||||
product_id,valid_from,valid_to,price
|
||||
9001,2023-12-01,2024-01-31,100
|
||||
9001,2024-02-01,2999-12-31,110
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ЧАСТЬ D. DDL-скелеты (минимально)
|
||||
|
||||
```sql
|
||||
-- DDS: измерение клиента (Type 2)
|
||||
CREATE TABLE dds.dim_customer (
|
||||
customer_sk BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
customer_bk VARCHAR NOT NULL,
|
||||
email VARCHAR,
|
||||
phone VARCHAR,
|
||||
city VARCHAR,
|
||||
valid_from DATE NOT NULL,
|
||||
valid_to DATE,
|
||||
is_current BOOLEAN NOT NULL DEFAULT TRUE
|
||||
);
|
||||
|
||||
-- DDS: факт продаж (гранулярность: строка заказа)
|
||||
CREATE TABLE dds.fact_sales (
|
||||
sale_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
||||
customer_sk BIGINT NOT NULL,
|
||||
product_sk BIGINT NOT NULL,
|
||||
date_sk INT NOT NULL,
|
||||
quantity INT NOT NULL,
|
||||
amount DECIMAL(18,2) NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ЧАСТЬ E. Текстовые альтернативы для «капризных» диаграмм
|
||||
|
||||
Если `mindmap`/`quadrantChart` не поддерживаются местом публикации, используйте пункты:
|
||||
|
||||
* **Сводка:**
|
||||
|
||||
* Архитектура: слои STG→ODS→DDS→DM, разделение ответственности, управляемость изменений.
|
||||
* Модели: 3NF (целостность), Data Vault (масштабируемость), Звезда (производительность), Anchor (гибкая эволюция).
|
||||
* Понятия: факты, измерения, SCD2, SK/BK.
|
||||
* Практика: понимать бизнес-задачу, выбирать подход под контекст, строить итеративно.
|
||||
|
||||
* **Интуитивное сравнение моделей (простыми словами):**
|
||||
Звезда — «быстро стартануть BI»; 3NF — «собрать единый словарь сущностей»; DV/Anchor — «надёжно интегрировать десятки источников и хранить всю историю», но читать сложнее.
|
||||
|
||||
---
|
||||
|
||||
## Итог
|
||||
|
||||
Статья даёт общий словарь и картинку мира: что за слои в DWH, какие модели бывают и зачем они нужны. Задача менти — почувствовать интуицию различий, научиться видеть место фактов/измерений/SCD и уверенно собрать простую витрину из DDS. Дальше — углубляться по нужной модели в отдельном материале (звезда/DV/Anchor/3NF).
|
||||
Reference in New Issue
Block a user