Стилистические правки
This commit is contained in:
@@ -1 +1,2 @@
|
|||||||
.env
|
.env
|
||||||
|
.idea
|
||||||
|
|||||||
+13
-18
@@ -1,10 +1,3 @@
|
|||||||
Отлично! Вот **связный, литературный и педагогически выстроенный** вариант статьи, адаптированный под новичков (студентов с базовым SQL), но без упрощения сути. Я убрал «рыхлые» места, усилил плавность повествования, добавил пояснения «на пальцах» — и сохранил все важные детали: архитектурные слои, модели данных, SCD, ключи, антипаттерны.
|
|
||||||
|
|
||||||
Статья оформлена как **обучающий материал** — с логикой «от боли к решению», с живым примером (интернет-магазин), с акцентом на *почему*, а не только *как*.
|
|
||||||
Готова к публикации на Habr, с приложенным GitHub-репозиторием.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# **Хранилище данных: как устроена аналитика «под капотом»**
|
# **Хранилище данных: как устроена аналитика «под капотом»**
|
||||||
*Для тех, кто знает SQL, но хочет понять, как хранить данные не в Excel, а по-взрослому*
|
*Для тех, кто знает SQL, но хочет понять, как хранить данные не в Excel, а по-взрослому*
|
||||||
|
|
||||||
@@ -132,12 +125,12 @@ flowchart TD
|
|||||||
|
|
||||||
Давайте проследим, как превращается строка заказа.
|
Давайте проследим, как превращается строка заказа.
|
||||||
|
|
||||||
### **STG (Staging / «Бронза»)** — «как пришло»
|
### **STG (Staging / Bronze)** — «как пришло»
|
||||||
|
|
||||||
- Таблицы: `stg.orders_raw`, `stg.customers_raw`;
|
- Таблицы: `stg.orders_raw`, `stg.customers_raw`;
|
||||||
- Структура — *точно как в источнике* (может быть `VARCHAR` даже у дат);
|
- Структура — *точно как в источнике* (может быть `VARCHAR` даже у дат);
|
||||||
- Добавлены технические поля:
|
- Добавлены технические поля:
|
||||||
- `_ingest_id` — идентификатор загрузки;
|
- `_load_id` — идентификатор загрузки;
|
||||||
- `_load_ts` — время получения данных;
|
- `_load_ts` — время получения данных;
|
||||||
- Главное правило: **неизменяемость**. Если пришла новая порция — либо добавляем новые строки, либо *полностью перезагружаем* слой (идемпотентность).
|
- Главное правило: **неизменяемость**. Если пришла новая порция — либо добавляем новые строки, либо *полностью перезагружаем* слой (идемпотентность).
|
||||||
- Дедупликация: если два раза пришёл один и тот же заказ — оставляем один (по `order_id + _load_ts`).
|
- Дедупликация: если два раза пришёл один и тот же заказ — оставляем один (по `order_id + _load_ts`).
|
||||||
@@ -146,7 +139,7 @@ flowchart TD
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### **ODS (Operational Data Store / «Серебро»)** — «почистили, но не трогали смысл»
|
### **ODS (Operational Data Store / Silver)** — «почистили, но не трогали смысл»
|
||||||
|
|
||||||
- Таблицы: `ods.orders`, `ods.customers`;
|
- Таблицы: `ods.orders`, `ods.customers`;
|
||||||
- Здесь:
|
- Здесь:
|
||||||
@@ -161,7 +154,7 @@ flowchart TD
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### **DDS (Data Delivery Store / «Ядро», «Золото»)** — «интеграция + история»
|
### **DDS (Data Delivery Store / Core / Gold)** — «интеграция + история»
|
||||||
|
|
||||||
Здесь рождается *единая бизнес-модель*.
|
Здесь рождается *единая бизнес-модель*.
|
||||||
Появляются понятия: **измерения**, **факты**, **суррогатные ключи**, **SCD**.
|
Появляются понятия: **измерения**, **факты**, **суррогатные ключи**, **SCD**.
|
||||||
@@ -192,7 +185,7 @@ flowchart TD
|
|||||||
- `dm.mart_daily_sales` — ежедневные продажи по товарам и сегментам;
|
- `dm.mart_daily_sales` — ежедневные продажи по товарам и сегментам;
|
||||||
- `dm.mart_customer_360` — полный портрет клиента: сколько потратил, когда заходил, какие товары любит.
|
- `dm.mart_customer_360` — полный портрет клиента: сколько потратил, когда заходил, какие товары любит.
|
||||||
|
|
||||||
Они построены по модели **Звезда (Star Schema)** — потому что BI-инструментам так удобнее всего.
|
Они часто построены по модели **Звезда (Star Schema)** — потому что BI-инструментам так удобнее всего.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -257,7 +250,7 @@ fact_sales.order_date BETWEEN dim_customer.valid_from AND dim_customer.valid_to
|
|||||||
```
|
```
|
||||||
и получаем актуальный на тот день email и город.
|
и получаем актуальный на тот день email и город.
|
||||||
|
|
||||||
> 🔍 Подробнее про SCD — в отдельной статье [SQC](SCD.md) (сравнение Type 1/2/3, паттерны обновления).
|
> 🔍 Подробнее про SCD — в отдельной статье [Slow Changing Dimensions](SCD.md) (сравнение Type 1/2/3, паттерны обновления).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -269,16 +262,18 @@ fact_sales.order_date BETWEEN dim_customer.valid_from AND dim_customer.valid_to
|
|||||||
*Источник: Билл Инмон (Bill Inmon)*
|
*Источник: Билл Инмон (Bill Inmon)*
|
||||||
|
|
||||||
✅ **Плюсы**:
|
✅ **Плюсы**:
|
||||||
- Максимальная **целостность** — дубликатов нет (все атрибуты — там, где должны быть);
|
- Минимум избыточности при строгих ключах и правилах дедупликации.
|
||||||
- Легко **расширять** — добавили новый источник? Расширили связанный справочник.
|
- Проще поддерживать единую терминологию и НСИ (reference data).
|
||||||
|
- Атрибуты и справочники легко расширять.
|
||||||
|
|
||||||
❌ **Минусы**:
|
❌ **Минусы**:
|
||||||
- Запросы сложные: много JOIN’ов даже для простого отчёта;
|
- Много JOIN даже для простых отчётов.
|
||||||
- Тяжело новичкам: «а где город клиента?» → нужно пройти `customer → address → city`.
|
- Историчность (SCD2) усложняет таблицы.
|
||||||
|
- Новые источники дороже гармонизировать (привести к канону).
|
||||||
|
|
||||||
📌 **Когда выбирать**:
|
📌 **Когда выбирать**:
|
||||||
→ Корпоративные DWH, где важна *единая терминология* и *долгосрочная поддержка*.
|
→ Корпоративные DWH, где важна *единая терминология* и *долгосрочная поддержка*.
|
||||||
→ Когда источников — десятки, и нужно гарантировать консистентность.
|
→ Стабильные домены (финансы, НСИ, договоры) и умеренная динамика изменений.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ WITH src AS (
|
|||||||
s._load_id,
|
s._load_id,
|
||||||
s._load_ts,
|
s._load_ts,
|
||||||
COALESCE(NULLIF(s.event_ts, '')::timestamp, s._load_ts,
|
COALESCE(NULLIF(s.event_ts, '')::timestamp, s._load_ts,
|
||||||
to_timestamp(regexp_replace(s._load_id,'^batch_',''),'YYYYMMDD_HH24MI')) AS eff_ts
|
to_timestamp(regexp_replace(s._load_id,'^batch_',''),'YYYYMMDD_HH24MI'))
|
||||||
|
AS eff_ts
|
||||||
FROM stg.customers_raw s
|
FROM stg.customers_raw s
|
||||||
WHERE s.customer_id ~ '^\d+$'
|
WHERE s.customer_id ~ '^\d+$'
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user