From 1f519dbc4b4ad4af032d4ed3bb5b7a5d0cf8f9bb Mon Sep 17 00:00:00 2001 From: orange13 Date: Thu, 23 Oct 2025 13:19:24 +0300 Subject: [PATCH 1/8] initial scd2 --- .../analyst/practical-guides/scd/index.md | 2 + .../practical-guides/scd/scd2-merge.md | 325 ++++++++++++++++++ .../analyst/practical-guides/scd/toc_p.yaml | 4 +- 3 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md diff --git a/ydb/docs/ru/core/analyst/practical-guides/scd/index.md b/ydb/docs/ru/core/analyst/practical-guides/scd/index.md index 92eb36597448..f564b58de6da 100644 --- a/ydb/docs/ru/core/analyst/practical-guides/scd/index.md +++ b/ydb/docs/ru/core/analyst/practical-guides/scd/index.md @@ -32,3 +32,5 @@ * С использованием связки [Change Data Capture (CDC)](../../../concepts/cdc.md) и [Transfer](../../../concepts/transfer.md) для автоматической потоковой репликации изменений из таблиц-источников. * [{#T}](scd1-transfer.md) * [{#T}](scd2-transfer.md) +* С помощью периодических YQL-запросов, которые обрабатывают пакеты изменений из промежуточной таблицы и подмерживают их в основную SCD-таблицу. + * [{#T}](scd2-merge.md) \ No newline at end of file diff --git a/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md b/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md new file mode 100644 index 000000000000..dd47e5e65b87 --- /dev/null +++ b/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md @@ -0,0 +1,325 @@ +# Реализация SCD2 в YDB + +В этой статье описывается реализация паттерна Slowly Changing Dimensions Type 2 (SCD2) в YDB с использованием процесса подмерживания изменений. + +## Используемые инструменты + +Для поставки данных в SCD2 таблицу в данной статье будет использоваться следующая комбинация из доступной в {{ ydb-short-name }} функциональности: + +1. Таблица-источник `dimension_scd_changes`, содержащая информацию об атрибутах, их значениях и моментах изменений данных. +1. [Колоночная таблица-приёмник](../../../concepts/datamodel/table.md#column-oriented-table) `dimension_scd2_final` для эффективной работы с аналитическими данными. +1. Периодически внешнее приложение должно вызывать запрос, который будет подмерживать изменения данных, накопившиеся в таблице `dimension_scd_changes`, в таблицу `dimension_scd2_final`. +1. Для поставки данных из OLTP-таблиц для хранения их в формате SCD2 удобно использовать встроенный в {{ydb-short-name}} механизм [трансфер](../../../concepts/transfer.md). + +## Создание таблицы для приёма всех изменений `dimension_scd_changes` + +```sql +CREATE TABLE dimension_scd_changes ( + id Utf8 NOT NULL, -- Бизнес-ключ + attribute1 Utf8, -- Атрибут данных + attribute2 Utf8, -- Атрибут данных + change_time Timestamp NOT NULL, -- Момент изменения данных + operation Utf8 -- Тип изменений данных + PRIMARY KEY (change_time, id) +) +``` + +Таблица `dimension_scd_changes` создается в [строковом варианте](../../../concepts/datamodel/table.md#row-oriented-tables), так как строковые таблицы обеспечивают эффективную работу с постоянно поступающими и удаляющимися данными изменений. + +Описание полей таблицы: + +- `id` — бизнес-ключ записи; +- `attribute1`, `attribute2` — атрибуты измерения; +- `change_time` — момент времени изменения данных; +- `operation` — тип изменения данных: `CREATE`, `UPDATE`, `DELETE`. + +Первичный ключ создается как `PRIMARY KEY (change_time, id)`, так как по одному и тому же ключу данных может происходить множество изменений и все эти изменения по одному ключу важно сохранять. + +## Создание финальной SCD2 таблицы `dimension_scd2_final` + +```sql +CREATE TABLE dimension_scd2_final ( + id Utf8 NOT NULL, -- Бизнес-ключ данных + attribute1 Utf8, -- Атрибут данных + attribute2 Utf8, -- Атрибут данных + valid_from Timestamp NOT NULL, -- Момент времени, с которого данные актуальны + valid_to Timestamp, -- Момент времени, до которого данные актуальны. + -- Если данные актуальны прямо сейчас, то в valid_to находится NULL + is_current Uint8, -- Признак, что данные актуальны прямо сейчас. + is_deleted Uint8, -- Признак, что данные были удалены. Если данные были удалены, то is_current = FALSE + PRIMARY KEY (valid_from, id) +) +PARTITION BY HASH(valid_from, id) +WITH( + STORE=COLUMN +) +``` + +Таблица `dimension_scd_changes` создается в [колоночном варианте](../../../concepts/datamodel/table.md#column-oriented-tables), так как колоночные таблицы обеспечивают эффективную работу с хранением редко изменяемых данных больших объемов на протяжении долгого времени. + +Описание полей таблицы: + +- `id` — бизнес-ключ записи; +- `attribute1`, `attribute2` — атрибуты измерения; +- `valid_from` — момент времени, с которого запись становится актуальной; +- `valid_to` — момент времени, до которого запись была актуальной (NULL для текущих записей); +- `is_current` — флаг, указывающий, является ли запись текущей (1) или исторической (0); +- `is_deleted` — флаг, указывающий, была ли запись удалена (1) или нет (0). + +Первичный ключ создается как `PRIMARY KEY (valid_from, id)`, так как по одному и тому же ключу данных может происходить множество изменений и все эти изменения по одному ключу важно сохранять. + +{% note info %} + +Таблицы `dimension_scd_changes`, `dimension_scd2_final` приведены для иллюстрации. Для реальных запросов вам нужно скорректировать структуру таблиц и их атрибутов. + +{% endnote %} + +## Загрузка данных в таблицу изменений + +Для загрузки данных в таблицу изменений можно использовать любой способ загрузки данных и автоматическую поставку изменений с помощью механизма [трансфер](../../../concepts/transfer.md). + +Пример запроса для явной загрузки изменений: + +```sql +UPSERT INTO dimension_scd_changes (id, attribute1, attribute2, change_time, operation) +VALUES ('CUSTOMER_1001', 'John Doe', 'Los Angeles', cast('2025-08-22T17:00:00Z' as DateTime), 'CREATE'); + +UPSERT INTO dimension_scd_changes (id, attribute1, attribute2, change_time, operation) +VALUES ('CUSTOMER_1002', 'John Doe', 'New York', cast('2025-08-22T17:00:00Z' as DateTime), 'CREATE'); + +UPSERT INTO dimension_scd_changes (id, attribute1, attribute2, change_time, operation) +VALUES ('CUSTOMER_1001', 'John Doe', 'San Francisco', cast('2025-08-22T19:00:00Z' as DateTime), 'UPDATE'); + +UPSERT INTO dimension_scd_changes (id, attribute1, attribute2, change_time, operation) +VALUES ('CUSTOMER_1002', 'John Doe', 'New York', cast('2025-08-22T21:00:00Z' as DateTime), 'DELETE'); + ``` + + +## Запрос для размещения изменений в формате SCD2 + +Для преобразования данных из таблицы изменений в формат SCD2 и загрузки в финальную таблицу используется следующий запрос: + +```sql +-- Шаг 1: Читаем все новые события из таблицы `dimension_scd_changes`. +-- Это именованное выражение ($changes) является исходным набором данных для всей последующей обработки в рамках этого запуска. +$changes = ( + SELECT + id, + attribute1, + attribute2, + change_time, + String::AsciiToUpper(operation) AS op + FROM dimension_scd_changes +); + +-- Шаг 2: Фильтруем события, оставляя только те, которых еще нет в целевой таблице. +-- Цель этого шага - обеспечить идемпотентность на уровне чтения, чтобы не обрабатывать +-- уже загруженные данные в случае сбоя и перезапуска скрипта. +$unprocessed_data = ( + SELECT + chg.id AS id, + chg.attribute1 AS attribute1, + chg.attribute2 AS attribute2, + chg.change_time AS change_time, + chg.op AS op + FROM $changes AS chg + LEFT JOIN dimension_scd2_final AS scd + ON chg.id = scd.id AND chg.change_time = scd.valid_from -- Ищем записи по каждой сущности (id) и времени изменения + WHERE scd.id IS NULL -- для исключения строк, которые уже были перенесены в таблицу dimension_scd2_final ранее +); + +-- Шаг 3: Находим в целевой таблице активные записи (`is_current=1`), для которых пришли обновления. +-- Формируем для них "закрывающие" версии, устанавливая `valid_to` равным времени +-- самого первого изменения из новой пачки ($unprocessed_data). +$close_open_intervals = ( + SELECT + target.id AS id, + target.attribute1 as attribute1, + target.attribute2 as attribute2, + target.valid_from as valid_from, + 0ut AS is_current, -- Закрываемая запись больше не является текущей + unprocessed_data.change_time AS valid_to, + target.is_deleted as is_deleted + FROM dimension_scd2_final AS target + INNER JOIN ( + SELECT + id, + MIN(change_time) AS change_time + FROM $unprocessed_data + GROUP BY id + ) AS unprocessed_data + ON target.id = unprocessed_data.id + WHERE target.is_current = 1ut +); + +-- Шаг 4: Преобразуем поток необработанных событий в версионные записи (строки для вставки). +-- Здесь вычисляются все необходимые атрибуты для новых версий: `valid_to`, `is_current`, `is_deleted`. +$updated_data = ( + SELECT + t.id AS id, + t.attribute1 AS attribute1, + t.attribute2 AS attribute2, + t.is_deleted AS is_deleted, + -- Логика флага `is_current`: он устанавливается в 1 только для последней + -- записи в цепочке (`next_change_time IS NULL`), и только если это не + -- операция удаления (`is_deleted == 0`). + IF(t.next_change_time IS NOT NULL OR t.is_deleted == 1ut, 0ut, 1ut) AS is_current, + t.change_time AS valid_from, + t.next_change_time AS valid_to + FROM ( + -- Подзапрос вычисляет для каждой строки флаг удаления (`is_deleted`) + -- и временную метку следующего события (`next_change_time`) с помощью оконной функции LEAD. + SELECT + unprocessed_data.id AS id, + unprocessed_data.attribute1 AS attribute1, + unprocessed_data.attribute2 AS attribute2, + unprocessed_data.op AS op, + unprocessed_data.change_time AS change_time, + IF(unprocessed_data.op = "DELETE", 1ut, 0ut) AS is_deleted, + LEAD(unprocessed_data.change_time) OVER (PARTITION BY id ORDER BY unprocessed_data.change_time) AS next_change_time + FROM $unprocessed_data AS unprocessed_data + ) AS t +); + +-- Шаг 5: Атомарно применяем все рассчитанные изменения к целевой таблице. +-- UPSERT обновит существующие записи (из $close_open_intervals) и вставит новые (из $updated_data). +UPSERT INTO dimension_scd2_final (id, attribute1, attribute2, is_current, is_deleted, valid_from, valid_to) +SELECT + id, + attribute1, + attribute2, + is_current, + is_deleted, + valid_from, + valid_to +FROM $close_open_intervals +UNION ALL +SELECT + id, + attribute1, + attribute2, + is_current, + is_deleted, + valid_from, + valid_to +FROM $updated_data; + +-- Шаг 6: Очищает стейджинг-таблицу от обработанных записей. +DELETE FROM dimension_scd_changes ON +SELECT id, change_time FROM $changes; +``` + +## Демонстрация работы + +В примере ниже рассматривается сущность **Customer**: + +- бизнес-ключ — поле `id`, +- атрибуты — `attribute1` (полное имя) и `attribute2` (город). + +В момент времени `2025-08-22 17:00` создаются два клиента (John в Los Angeles и Judy в New York), в момент времени `2025-08-22 19:00` клиент `CUSTOMER_1001` меняет город на San Francisco (UPDATE), а в момент `2025-08-22 21:00` клиент `CUSTOMER_1002` удаляется (DELETE). + +| id | attribute1 | attribute2 | change\_time | operation | +| -------------- | ---------- | ------------- | ---------------- | --------- | +| CUSTOMER\_1001 | John Doe | Los Angeles | 2025-08-22 17:00 | CREATE | +| CUSTOMER\_1002 | Judy Doe | New York | 2025-08-22 17:00 | CREATE | +| CUSTOMER\_1001 | John Doe | San Francisco | 2025-08-22 19:00 | UPDATE | +| CUSTOMER\_1002 | Judy Doe | New York | 2025-08-22 21:00 | DELETE | + +Процесс SCD2 превращает эти события в версионные интервальные записи **\[valid\_from, valid\_to)**: у `CUSTOMER_1001` образуются две последовательные версии (LA → SF, текущая с `valid_to = NULL`), у `CUSTOMER_1002` — историческая версия и финальная «тумбстоун»-запись с `is_deleted=1` и `is_current=0`. + +Ниже показаны исходные события и соответствующие им версии в финальной таблице. + +```mermaid + gantt + title История изменения данных + dateFormat YYYY-MM-DD HH:mm + axisFormat %H:%M + todayMarker off + + section CUSTOMER_1001 — John Doe + Los Angeles :done, t0, 2025-08-22 17:00, 2025-08-22 19:00 + San Francisco (is_current) :active, t1, 2025-08-22 19:00, 2025-08-23 00:00 + + section CUSTOMER_1002 — Judy Doe + New York :done, t0b, 2025-08-22 17:00, 2025-08-22 21:00 + +``` + +| id | attribute1 | attribute2 | valid\_from | valid\_to | is\_current | is\_deleted | +| -------------- | ---------- | ------------- | ---------------- | ---------------- | ----------- | ----------- | +| CUSTOMER\_1001 | John Doe | Los Angeles | 2025-08-22 17:00 | 2025-08-22 19:00 | 0 | 0 | +| CUSTOMER\_1001 | John Doe | San Francisco | 2025-08-22 19:00 | NULL | 1 | 0 | +| CUSTOMER\_1002 | Judy Doe | New York | 2025-08-22 17:00 | 2025-08-22 21:00 | 0 | 0 | +| CUSTOMER\_1002 | Judy Doe | New York | 2025-08-22 21:00 | NULL | 0 | 1 | + + +## Получение данных из SCD2-таблицы + +### Получение актуальных данных + +```sql +SELECT + id, + attribute1, + attribute2, + valid_from, + valid_to +FROM dimension_scd2_final +WHERE is_current = 1ut; +``` + +Результат: + +| id | attribute1 | attribute2 | valid\_from | valid\_to | is\_current | is\_deleted | +| -------------- | ---------- | ------------- | ---------------- | ---------------- | ----------- | ----------- | +| CUSTOMER\_1001 | John Doe | San Francisco | 2025-08-22 19:00 | NULL | 1 | 0 | + + +### Получение данных на определённый момент времени + +```sql +DECLARE $as_of AS Timestamp; +$as_of = Timestamp("2025-08-22T19:11:30.000000Z"); + +SELECT + id, + attribute1, + attribute2, + valid_from, + valid_to +FROM dimension_scd2_final +WHERE valid_from <= $as_of + AND (valid_to IS NULL OR valid_to > $as_of) -- Получаем записи, которые действовали в $as_of момент времени + AND is_deleted = 0ut -- Только записи, которые не удалены +``` + +Результат: + +| id | attribute1 | attribute2 | valid\_from | valid\_to | +| -------------- | ---------- | ------------- | ---------------- | ---------------- | +| CUSTOMER\_1001 | John Doe | San Francisco | 2025-08-22 19:00 | NULL | +| CUSTOMER\_1002 | Judy Doe | New York | 2025-08-22 17:00 | 2025-08-22 21:00 | + + +### Получение истории изменений для конкретной записи + +```sql +SELECT + id, + attribute1, + attribute2, + valid_from, + valid_to, + is_current, + is_deleted +FROM dimension_scd2_final +WHERE id = 'CUSTOMER_1001' +ORDER BY valid_from; +``` + +Результат: + +| id | attribute1 | attribute2 | valid\_from | valid\_to | is\_current | is\_deleted | +| -------------- | ---------- | ------------- | ---------------- | ---------------- | ----------- | ----------- | +| CUSTOMER\_1001 | John Doe | Los Angeles | 2025-08-22 17:00 | 2025-08-22 19:00 | 0 | 0 | +| CUSTOMER\_1001 | John Doe | San Francisco | 2025-08-22 19:00 | NULL | 1 | 0 | diff --git a/ydb/docs/ru/core/analyst/practical-guides/scd/toc_p.yaml b/ydb/docs/ru/core/analyst/practical-guides/scd/toc_p.yaml index 9411a0d1431e..cdab45e14891 100644 --- a/ydb/docs/ru/core/analyst/practical-guides/scd/toc_p.yaml +++ b/ydb/docs/ru/core/analyst/practical-guides/scd/toc_p.yaml @@ -4,4 +4,6 @@ items: - name: SCD1 с использованием TRANSFER href: scd1-transfer.md - name: SCD2 с использованием TRANSFER - href: scd2-transfer.md \ No newline at end of file + href: scd2-transfer.md + - name: SCD2 в YDB + href: scd2-merge.md \ No newline at end of file From eed7038765a17905d3e4534e59e0b5d4762b8182 Mon Sep 17 00:00:00 2001 From: orange13 Date: Mon, 27 Oct 2025 15:05:33 +0300 Subject: [PATCH 2/8] Update scd2-merge.md --- .../core/analyst/practical-guides/scd/scd2-merge.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md b/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md index dd47e5e65b87..f9691796545d 100644 --- a/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md +++ b/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md @@ -19,7 +19,7 @@ CREATE TABLE dimension_scd_changes ( attribute1 Utf8, -- Атрибут данных attribute2 Utf8, -- Атрибут данных change_time Timestamp NOT NULL, -- Момент изменения данных - operation Utf8 -- Тип изменений данных + operation Utf8, -- Тип изменений данных PRIMARY KEY (change_time, id) ) ``` @@ -82,17 +82,17 @@ WITH( ```sql UPSERT INTO dimension_scd_changes (id, attribute1, attribute2, change_time, operation) -VALUES ('CUSTOMER_1001', 'John Doe', 'Los Angeles', cast('2025-08-22T17:00:00Z' as DateTime), 'CREATE'); +VALUES ('CUSTOMER_1001', 'John Doe', 'Los Angeles', unwrap(cast('2025-08-22T17:00:00Z' as Timestamp)), 'CREATE'); UPSERT INTO dimension_scd_changes (id, attribute1, attribute2, change_time, operation) -VALUES ('CUSTOMER_1002', 'John Doe', 'New York', cast('2025-08-22T17:00:00Z' as DateTime), 'CREATE'); +VALUES ('CUSTOMER_1002', 'John Doe', 'New York', unwrap(cast('2025-08-22T17:00:00Z' as Timestamp)), 'CREATE'); UPSERT INTO dimension_scd_changes (id, attribute1, attribute2, change_time, operation) -VALUES ('CUSTOMER_1001', 'John Doe', 'San Francisco', cast('2025-08-22T19:00:00Z' as DateTime), 'UPDATE'); +VALUES ('CUSTOMER_1001', 'John Doe', 'San Francisco', unwrap(cast('2025-08-22T19:00:00Z' as Timestamp)), 'UPDATE'); UPSERT INTO dimension_scd_changes (id, attribute1, attribute2, change_time, operation) -VALUES ('CUSTOMER_1002', 'John Doe', 'New York', cast('2025-08-22T21:00:00Z' as DateTime), 'DELETE'); - ``` +VALUES ('CUSTOMER_1002', 'John Doe', 'New York', unwrap(cast('2025-08-22T21:00:00Z' as Timestamp)), 'DELETE'); +``` ## Запрос для размещения изменений в формате SCD2 From ee5b21d80e2884d6e0b4ccf2c8e8e84accf5b129 Mon Sep 17 00:00:00 2001 From: orange13 Date: Tue, 28 Oct 2025 12:32:00 +0300 Subject: [PATCH 3/8] remove column info --- .../core/analyst/practical-guides/scd/scd2-merge.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md b/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md index f9691796545d..12fac97eac3e 100644 --- a/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md +++ b/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md @@ -1,13 +1,13 @@ -# Реализация SCD2 в YDB +# Использование процесса подмерживания изменения для реализация SCD2 в {{ ydb-full-name }} -В этой статье описывается реализация паттерна Slowly Changing Dimensions Type 2 (SCD2) в YDB с использованием процесса подмерживания изменений. +В этой статье описывается реализация паттерна Slowly Changing Dimensions Type 2 (SCD2) в {{ ydb-full-name }} с использованием процесса подмерживания изменений. ## Используемые инструменты Для поставки данных в SCD2 таблицу в данной статье будет использоваться следующая комбинация из доступной в {{ ydb-short-name }} функциональности: 1. Таблица-источник `dimension_scd_changes`, содержащая информацию об атрибутах, их значениях и моментах изменений данных. -1. [Колоночная таблица-приёмник](../../../concepts/datamodel/table.md#column-oriented-table) `dimension_scd2_final` для эффективной работы с аналитическими данными. +1. Таблица-приёмник `dimension_scd2_final` для хранения результирующих данных. 1. Периодически внешнее приложение должно вызывать запрос, который будет подмерживать изменения данных, накопившиеся в таблице `dimension_scd_changes`, в таблицу `dimension_scd2_final`. 1. Для поставки данных из OLTP-таблиц для хранения их в формате SCD2 удобно использовать встроенный в {{ydb-short-name}} механизм [трансфер](../../../concepts/transfer.md). @@ -22,10 +22,11 @@ CREATE TABLE dimension_scd_changes ( operation Utf8, -- Тип изменений данных PRIMARY KEY (change_time, id) ) +WITH( + STORE=COLUMN +) ``` -Таблица `dimension_scd_changes` создается в [строковом варианте](../../../concepts/datamodel/table.md#row-oriented-tables), так как строковые таблицы обеспечивают эффективную работу с постоянно поступающими и удаляющимися данными изменений. - Описание полей таблицы: - `id` — бизнес-ключ записи; @@ -55,8 +56,6 @@ WITH( ) ``` -Таблица `dimension_scd_changes` создается в [колоночном варианте](../../../concepts/datamodel/table.md#column-oriented-tables), так как колоночные таблицы обеспечивают эффективную работу с хранением редко изменяемых данных больших объемов на протяжении долгого времени. - Описание полей таблицы: - `id` — бизнес-ключ записи; From 72ec449aedda59072188a1f6bdf405a2627fcd3f Mon Sep 17 00:00:00 2001 From: orange13 Date: Tue, 28 Oct 2025 13:47:21 +0300 Subject: [PATCH 4/8] Update scd2-merge.md --- ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md b/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md index 12fac97eac3e..489254e20151 100644 --- a/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md +++ b/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md @@ -1,4 +1,4 @@ -# Использование процесса подмерживания изменения для реализация SCD2 в {{ ydb-full-name }} +# Использование процесса подмерживания изменения для реализации SCD2 в {{ ydb-full-name }} В этой статье описывается реализация паттерна Slowly Changing Dimensions Type 2 (SCD2) в {{ ydb-full-name }} с использованием процесса подмерживания изменений. From 5b1dbc71de90bc4e109797ab6169f1534b2a6849 Mon Sep 17 00:00:00 2001 From: orange13 Date: Wed, 29 Oct 2025 17:15:17 +0300 Subject: [PATCH 5/8] Update scd2-merge.md --- .../practical-guides/scd/scd2-merge.md | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md b/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md index 489254e20151..6201ced8f987 100644 --- a/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md +++ b/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md @@ -1,6 +1,6 @@ # Использование процесса подмерживания изменения для реализации SCD2 в {{ ydb-full-name }} -В этой статье описывается реализация паттерна Slowly Changing Dimensions Type 2 (SCD2) в {{ ydb-full-name }} с использованием процесса подмерживания изменений. +В этой статье описывается реализация паттерна [Slowly Changing Dimensions Type 2 (SCD2)](./index.md#scd2) в {{ ydb-full-name }} с использованием процесса подмерживания изменений. ## Используемые инструменты @@ -9,7 +9,13 @@ 1. Таблица-источник `dimension_scd_changes`, содержащая информацию об атрибутах, их значениях и моментах изменений данных. 1. Таблица-приёмник `dimension_scd2_final` для хранения результирующих данных. 1. Периодически внешнее приложение должно вызывать запрос, который будет подмерживать изменения данных, накопившиеся в таблице `dimension_scd_changes`, в таблицу `dimension_scd2_final`. -1. Для поставки данных из OLTP-таблиц для хранения их в формате SCD2 удобно использовать встроенный в {{ydb-short-name}} механизм [трансфер](../../../concepts/transfer.md). +1. Для поставки данных из строковых таблиц для хранения их в формате SCD2 удобно использовать встроенный в {{ydb-short-name}} механизм [трансфера](../../../concepts/transfer.md). + +{% note info %} + +Таблицы `dimension_scd_changes`, `dimension_scd2_final` приведены для иллюстрации. Для реальных запросов вам нужно скорректировать структуру таблиц и их атрибутов. + +{% endnote %} ## Создание таблицы для приёма всех изменений `dimension_scd_changes` @@ -22,7 +28,7 @@ CREATE TABLE dimension_scd_changes ( operation Utf8, -- Тип изменений данных PRIMARY KEY (change_time, id) ) -WITH( +WITH ( STORE=COLUMN ) ``` @@ -34,7 +40,7 @@ WITH( - `change_time` — момент времени изменения данных; - `operation` — тип изменения данных: `CREATE`, `UPDATE`, `DELETE`. -Первичный ключ создается как `PRIMARY KEY (change_time, id)`, так как по одному и тому же ключу данных может происходить множество изменений и все эти изменения по одному ключу важно сохранять. +Первичный ключ создается как `PRIMARY KEY (change_time, id)`, так как по одному и тому же бизнес-ключу данных может происходить множество изменений и все эти изменения по одному ключу важно сохранять. ## Создание финальной SCD2 таблицы `dimension_scd2_final` @@ -61,18 +67,12 @@ WITH( - `id` — бизнес-ключ записи; - `attribute1`, `attribute2` — атрибуты измерения; - `valid_from` — момент времени, с которого запись становится актуальной; -- `valid_to` — момент времени, до которого запись была актуальной (NULL для текущих записей); -- `is_current` — флаг, указывающий, является ли запись текущей (1) или исторической (0); -- `is_deleted` — флаг, указывающий, была ли запись удалена (1) или нет (0). +- `valid_to` — момент времени, до которого запись была актуальной, или `NULL` для текущих записей; +- `is_current` — флаг, указывающий, является ли запись текущей (1 - текущая запись) или (0 - запись историческая); +- `is_deleted` — флаг, указывающий, была ли запись удалена (1 - запись была удалена) или (0 - запись не была удалена). Первичный ключ создается как `PRIMARY KEY (valid_from, id)`, так как по одному и тому же ключу данных может происходить множество изменений и все эти изменения по одному ключу важно сохранять. -{% note info %} - -Таблицы `dimension_scd_changes`, `dimension_scd2_final` приведены для иллюстрации. Для реальных запросов вам нужно скорректировать структуру таблиц и их атрибутов. - -{% endnote %} - ## Загрузка данных в таблицу изменений Для загрузки данных в таблицу изменений можно использовать любой способ загрузки данных и автоматическую поставку изменений с помощью механизма [трансфер](../../../concepts/transfer.md). @@ -81,16 +81,16 @@ WITH( ```sql UPSERT INTO dimension_scd_changes (id, attribute1, attribute2, change_time, operation) -VALUES ('CUSTOMER_1001', 'John Doe', 'Los Angeles', unwrap(cast('2025-08-22T17:00:00Z' as Timestamp)), 'CREATE'); +VALUES ('CUSTOMER_1001', 'John Doe', 'Los Angeles', Unwrap(CAST('2025-08-22T17:00:00Z' as Timestamp)), 'CREATE'); UPSERT INTO dimension_scd_changes (id, attribute1, attribute2, change_time, operation) -VALUES ('CUSTOMER_1002', 'John Doe', 'New York', unwrap(cast('2025-08-22T17:00:00Z' as Timestamp)), 'CREATE'); +VALUES ('CUSTOMER_1002', 'John Doe', 'New York', Unwrap(CAST('2025-08-22T17:00:00Z' as Timestamp)), 'CREATE'); UPSERT INTO dimension_scd_changes (id, attribute1, attribute2, change_time, operation) -VALUES ('CUSTOMER_1001', 'John Doe', 'San Francisco', unwrap(cast('2025-08-22T19:00:00Z' as Timestamp)), 'UPDATE'); +VALUES ('CUSTOMER_1001', 'John Doe', 'San Francisco', Unwrap(CAST('2025-08-22T19:00:00Z' as Timestamp)), 'UPDATE'); UPSERT INTO dimension_scd_changes (id, attribute1, attribute2, change_time, operation) -VALUES ('CUSTOMER_1002', 'John Doe', 'New York', unwrap(cast('2025-08-22T21:00:00Z' as Timestamp)), 'DELETE'); +VALUES ('CUSTOMER_1002', 'John Doe', 'New York', Unwrap(CAST('2025-08-22T21:00:00Z' as Timestamp)), 'DELETE'); ``` @@ -215,7 +215,7 @@ SELECT id, change_time FROM $changes; - бизнес-ключ — поле `id`, - атрибуты — `attribute1` (полное имя) и `attribute2` (город). -В момент времени `2025-08-22 17:00` создаются два клиента (John в Los Angeles и Judy в New York), в момент времени `2025-08-22 19:00` клиент `CUSTOMER_1001` меняет город на San Francisco (UPDATE), а в момент `2025-08-22 21:00` клиент `CUSTOMER_1002` удаляется (DELETE). +В момент времени `2025-08-22 17:00` создаются два клиента (John в Los Angeles и Judy в New York), в момент времени `2025-08-22 19:00` клиент `CUSTOMER_1001` меняет город на San Francisco `UPDATE`, а в момент `2025-08-22 21:00` клиент `CUSTOMER_1002` удаляется `DELETE`. | id | attribute1 | attribute2 | change\_time | operation | | -------------- | ---------- | ------------- | ---------------- | --------- | From d3883a676b19fea2ca83a8487f4470b766a92729 Mon Sep 17 00:00:00 2001 From: orange13 Date: Thu, 30 Oct 2025 15:00:15 +0300 Subject: [PATCH 6/8] Update scd2-merge.md --- .../ru/core/analyst/practical-guides/scd/scd2-merge.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md b/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md index 6201ced8f987..9d21b69624ca 100644 --- a/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md +++ b/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md @@ -28,6 +28,7 @@ CREATE TABLE dimension_scd_changes ( operation Utf8, -- Тип изменений данных PRIMARY KEY (change_time, id) ) +PARTITION BY HASH(change_time, id) WITH ( STORE=COLUMN ) @@ -40,7 +41,7 @@ WITH ( - `change_time` — момент времени изменения данных; - `operation` — тип изменения данных: `CREATE`, `UPDATE`, `DELETE`. -Первичный ключ создается как `PRIMARY KEY (change_time, id)`, так как по одному и тому же бизнес-ключу данных может происходить множество изменений и все эти изменения по одному ключу важно сохранять. +Первичный ключ создается как `PRIMARY KEY (change_time, id)`, так как по одному и тому же бизнес-ключу данных может происходить множество изменений и все эти изменения по одному ключу важно сохранять. Подробнее про выбор первичного ключа и ключа партиционирования, можно прочесть в документации - [выбор первичного ключа](../../../dev/primary-key/column-oriented.md##vybor-pervichnogo-klyucha), [выбор ключа партиционирования](../../../dev/primary-key/column-oriented.md##vybor-klyucha-particionirovaniya) ## Создание финальной SCD2 таблицы `dimension_scd2_final` @@ -96,7 +97,7 @@ VALUES ('CUSTOMER_1002', 'John Doe', 'New York', Unwrap(CAST('2025-08-22T21:00:0 ## Запрос для размещения изменений в формате SCD2 -Для преобразования данных из таблицы изменений в формат SCD2 и загрузки в финальную таблицу используется следующий запрос: +Чтобы преобразовать данные из таблицы изменений в формат SCD2 и загрузить их в финальную таблицу, используется специальный запрос. Этот запрос нужно запускать регулярно — с такой периодичностью, с какой вы хотите обновлять данные в финальной таблице. Для автоматического запуска можно воспользоваться [интеграцию](../../../integrations/orchestration/airflow.md) {{ ydb-short-name }} с Apache Airflow™: ```sql -- Шаг 1: Читаем все новые события из таблицы `dimension_scd_changes`. @@ -215,7 +216,7 @@ SELECT id, change_time FROM $changes; - бизнес-ключ — поле `id`, - атрибуты — `attribute1` (полное имя) и `attribute2` (город). -В момент времени `2025-08-22 17:00` создаются два клиента (John в Los Angeles и Judy в New York), в момент времени `2025-08-22 19:00` клиент `CUSTOMER_1001` меняет город на San Francisco `UPDATE`, а в момент `2025-08-22 21:00` клиент `CUSTOMER_1002` удаляется `DELETE`. +В момент времени `2025-08-22 17:00` создаются два клиента (John в Los Angeles - с id `CUSTOMER_1001` и Judy в New York с id `CUSTOMER_1002`), в момент времени `2025-08-22 19:00` клиент `CUSTOMER_1001` меняет город на San Francisco `UPDATE`, а в момент `2025-08-22 21:00` клиент `CUSTOMER_1002` удаляется `DELETE`. | id | attribute1 | attribute2 | change\_time | operation | | -------------- | ---------- | ------------- | ---------------- | --------- | @@ -224,7 +225,7 @@ SELECT id, change_time FROM $changes; | CUSTOMER\_1001 | John Doe | San Francisco | 2025-08-22 19:00 | UPDATE | | CUSTOMER\_1002 | Judy Doe | New York | 2025-08-22 21:00 | DELETE | -Процесс SCD2 превращает эти события в версионные интервальные записи **\[valid\_from, valid\_to)**: у `CUSTOMER_1001` образуются две последовательные версии (LA → SF, текущая с `valid_to = NULL`), у `CUSTOMER_1002` — историческая версия и финальная «тумбстоун»-запись с `is_deleted=1` и `is_current=0`. +Процесс SCD2 преобразует такие события в интервальные версии записей с полями `valid_from` и `valid_to`. Например, у `CUSTOMER_1001` получится две последовательные версии: сначала с городом LA, затем с городом SF (актуальная запись, у которой `valid_to = NULL`). У `CUSTOMER_1002` будет одна устаревшая версия и последняя запись с флагами `is_deleted=1` и `is_current=0`, которая показывает, что пользователь удалён. Ниже показаны исходные события и соответствующие им версии в финальной таблице. @@ -277,7 +278,6 @@ WHERE is_current = 1ut; ### Получение данных на определённый момент времени ```sql -DECLARE $as_of AS Timestamp; $as_of = Timestamp("2025-08-22T19:11:30.000000Z"); SELECT From 0a5b9de58a4f10c69d6c060440edb5791230b801 Mon Sep 17 00:00:00 2001 From: orange13 Date: Thu, 30 Oct 2025 15:03:09 +0300 Subject: [PATCH 7/8] Update toc_p.yaml --- ydb/docs/ru/core/analyst/practical-guides/scd/toc_p.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ydb/docs/ru/core/analyst/practical-guides/scd/toc_p.yaml b/ydb/docs/ru/core/analyst/practical-guides/scd/toc_p.yaml index cdab45e14891..3eacfd53e9b9 100644 --- a/ydb/docs/ru/core/analyst/practical-guides/scd/toc_p.yaml +++ b/ydb/docs/ru/core/analyst/practical-guides/scd/toc_p.yaml @@ -5,5 +5,5 @@ items: href: scd1-transfer.md - name: SCD2 с использованием TRANSFER href: scd2-transfer.md - - name: SCD2 в YDB + - name: SCD2 с использованием подмерживания изменений href: scd2-merge.md \ No newline at end of file From 196a5d32785fe705297af36c9b0f993e9beb54b8 Mon Sep 17 00:00:00 2001 From: orange13 Date: Thu, 30 Oct 2025 15:22:58 +0300 Subject: [PATCH 8/8] Update scd2-merge.md --- ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md b/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md index 9d21b69624ca..b175676d4c99 100644 --- a/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md +++ b/ydb/docs/ru/core/analyst/practical-guides/scd/scd2-merge.md @@ -1,4 +1,4 @@ -# Использование процесса подмерживания изменения для реализации SCD2 в {{ ydb-full-name }} +# Использование процесса подмерживания изменения для реализации SCD2 в {{ ydb-full-name }} В этой статье описывается реализация паттерна [Slowly Changing Dimensions Type 2 (SCD2)](./index.md#scd2) в {{ ydb-full-name }} с использованием процесса подмерживания изменений.