diff --git a/SCD.md b/SCD.md index 48ecdd4..8d674dc 100644 --- a/SCD.md +++ b/SCD.md @@ -230,7 +230,7 @@ WHERE customer_id = 1 AND is_current = true --- -### А что, если СУБД не позволяет UPDATE? (Trino, Hive, ClickHouse в режиме append-only) +#### А что, если СУБД не позволяет UPDATE? (Trino, Hive, ClickHouse в режиме append-only) Некоторые аналитические системы (например, **Hive в формате ORC/Parquet**, **Trino**, **ClickHouse в режиме только вставки**) **не поддерживают UPDATE старых строк**. Как тогда реализовать SCD Type 2? @@ -238,7 +238,7 @@ WHERE customer_id = 1 AND is_current = true Алгоритм загрузки (ETL): 1. Сравнить входящие данные с последней версией в таблице. -2. Если есть изменения — **пишем новую строку** с новыми `valid_from` / `valid_to`. +2. Если есть изменения — **пишем новую строку** с новыми `valid_from`. 3. Старые строки остаются нетронутыми. Пример в Trino/Hive-стиле (только INSERT): @@ -269,9 +269,9 @@ LEFT JOIN current_customers c ON n.customer_id = c.customer_id WHERE c.customer_id IS NULL OR c.category != n.category; -- условие верно, если какие-то из полей справочника поменялись ``` -### 🎯 Как работает этот запрос: пошаговое объяснение +##### 🎯 Как работает этот запрос: пошаговое объяснение -#### Шаг 1: Подготовка данных (CTE current_customers) +###### Шаг 1: Подготовка данных (CTE current_customers) CTE `current_customers` находит **последнюю версию** каждого клиента из таблицы `dim_customers_scd2`: ```sql @@ -283,7 +283,7 @@ FROM dim_customers_scd2 - `ORDER BY valid_from DESC` — сортируем версии от самой новой к самой старой - `WHERE rn = 1` — выбираем только самую свежую версию -#### Шаг 2: Сравнение данных (LEFT JOIN + WHERE) +###### Шаг 2: Сравнение данных (LEFT JOIN + WHERE) Теперь сравниваем новые данные с текущими: ```sql @@ -299,9 +299,10 @@ LEFT JOIN current_customers c ON n.customer_id = c.customer_id | Категория изменилась | 1 | 1 | ✅ `c.category != n.category` | Вставляется | | Без изменений | 3 | 3 | ❌ оба условия ложны | Пропускается | -#### Шаг 3: Вставка новых версий +###### Шаг 3: Вставка новых версий Для подходящих записей создаём новую версию: -- `uuid()` — генерируем уникальный ключ для новой версии (функция, скобки обязательны) +- `uuid()` — генерируем уникальный ключ для новой версии +- `current_date` - функция, возвращающая текущую даты - `COALESCE(n.effective_date, current_date)` — устанавливаем дату начала действия новой версии > 💡 **Правильный подход к датам**: В реальных ETL-процессах важно использовать дату из исходных данных, когда она доступна. Мы используем `COALESCE(n.effective_date, current_date)`, что означает: @@ -320,7 +321,7 @@ LEFT JOIN current_customers c ON n.customer_id = c.customer_id > 1 | Иван Петров | VIP | 2025-04-15 ← дата реального изменения > ``` -#### Практический пример +##### Практический пример **До выполнения запроса:** ``` @@ -348,11 +349,11 @@ customer_id | name | category | valid_from > 💡 **Ключевой момент**: В append-only системах мы **не обновляем** старые записи, а только **добавляем новые**. История сохраняется автоматически! -**Главный вопрос после загрузки: как же читать эти данные?** +##### Главный вопрос после загрузки: как же читать эти данные? -Поскольку мы не можем обновлять `valid_to` у предыдущей версии (у неё останется `NULL`), стандартный подход с `BETWEEN` не сработает. Вместо этого, для поиска нужной версии мы полагаемся на **оконные функции** или на логику «ближайшей даты, но не позже». +Поскольку мы не можем обновлять `valid_to` у предыдущей версии, стандартный подход с `BETWEEN` не сработает. Вместо этого, для поиска нужной версии мы полагаемся на **оконные функции** или на логику «ближайшей даты, но не позже». -#### Паттерн 1: Найти последнюю (актуальную) версию на сегодня +###### Паттерн 1: Найти последнюю (актуальную) версию на сегодня Это самый частый запрос. Мы хотим видеть самую свежую информацию о клиенте. @@ -372,7 +373,7 @@ WHERE rn = 1; Этот запрос берёт строку с самой поздней датой начала действия для каждого клиента. -#### Паттерн 2: Найти версию, которая была актуальна на конкретную дату +###### Паттерн 2: Найти версию, которая была актуальна на конкретную дату Это основная цель SCD2. Например, «какой статус клиента был на дату заказа `2025-03-15`?». @@ -434,10 +435,9 @@ WHERE rn = 1; Медленно меняющиеся измерения — это не «магия», а **практический инструмент** для честной и точной аналитики во времени. -Начните с понимания разницы между Type 1 и Type 2. Попробуйте реализовать оба подхода в своём PostgreSQL. Задайте себе вопрос: +Начните с понимания разницы между Type 1 и Type 2. Попробуйте реализовать оба подхода в своей БД. Задайте себе вопрос: > «Если бы я построил отчёт по данным на прошлый месяц — дал бы он правильный ответ после сегодняшнего изменения?» Если нет — вам нужен SCD Type 2. И помните: даже в системах без `UPDATE` вы можете хранить полную историю — достаточно понимать, как правильно читать данные с помощью оконных функций и временных границ. -