<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0"
  xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  xmlns:atom="http://www.w3.org/2005/Atom">

<channel>

<title>Yuriy Gavrilov: posts tagged dbt</title>
<link>https://gavrilov.info/tags/dbt/</link>
<description>Welcome to my personal place for love, peace and happiness 🤖 Yuiry Gavrilov</description>
<author></author>
<language>en</language>
<generator>Aegea 11.4 (v4171e)</generator>

<itunes:owner>
<itunes:name></itunes:name>
<itunes:email>yvgavrilov@gmail.com</itunes:email>
</itunes:owner>
<itunes:subtitle>Welcome to my personal place for love, peace and happiness 🤖 Yuiry Gavrilov</itunes:subtitle>
<itunes:image href="https://gavrilov.info/pictures/userpic/userpic-square@2x.jpg?1643451008" />
<itunes:explicit>no</itunes:explicit>

<item>
<title>dbt открывает исходный код MetricFlow: Управляемые метрики для AI и аналитики</title>
<guid isPermaLink="false">292</guid>
<link>https://gavrilov.info/all/dbt-otkryvaet-ishodny-kod-metricflow-upravlyaemye-metriki-dlya-a/</link>
<pubDate>Sat, 01 Nov 2025 01:03:55 +0300</pubDate>
<author></author>
<comments>https://gavrilov.info/all/dbt-otkryvaet-ishodny-kod-metricflow-upravlyaemye-metriki-dlya-a/</comments>
<description>
&lt;p&gt;Компания dbt Labs объявила о важном изменении в своей стратегии: `MetricFlow`, ключевая технология, лежащая в основе `dbt Semantic Layer`, становится полностью открытой. Проект переводится под лицензию Apache 2.0, что позволяет любому использовать, изменять и встраивать его в свои продукты. Это стратегический шаг, направленный на создание единого отраслевого стандарта для определения бизнес-метрик, особенно в свете бурного развития AI-систем.&lt;/p&gt;
&lt;p&gt;Оригинал тут: &lt;a href="https://www.getdbt.com/blog/open-source-metricflow-governed-metrics"&gt;https://www.getdbt.com/blog/open-source-metricflow-governed-metrics&lt;/a&gt;&lt;br /&gt;
А гит тут: &lt;a href="https://github.com/dbt-labs/metricflow"&gt;https://github.com/dbt-labs/metricflow&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Еще кстати есть &lt;a href="https://github.com/memiiso/opendbt"&gt;https://github.com/memiiso/opendbt&lt;/a&gt; ( Make dbt great again! :) Может они сольются с метриками, интересно.&lt;/p&gt;
&lt;h3&gt;Проблема: почему семантический слой стал критически важен&lt;/h3&gt;
&lt;p&gt;Концепция семантического слоя, который служит промежуточным слоем для определения бизнес-логики (метрик, измерений, связей), не нова. Она уже много лет используется в BI-системах для обеспечения согласованности отчетов. Однако с появлением больших языковых моделей (LLM) и инструментов в стиле “Chat with your data” проблема вышла на новый уровень.&lt;/p&gt;
&lt;p&gt;Когда AI-агент или LLM пытается ответить на вопрос, обращаясь напрямую к базе данных, он вынужден самостоятельно генерировать SQL-запрос. При этом модель “угадывает”, какие таблицы нужно соединить (`JOIN`), как правильно отфильтровать данные, какую использовать гранулярность по времени и какие оконные функции применить.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Проблемы такого подхода:&lt;/b&gt;&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;b&gt;Несогласованность:&lt;/b&gt; Две разные модели (или даже одна и та же, но с другим запросом) могут сгенерировать разный SQL для расчета, казалось бы, одной и той же метрики. Это приводит к разным цифрам в отчетах.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Ошибки:&lt;/b&gt; LLM может не знать о тонкостях бизнес-логики, например, о том, что при расчете выручки нужно учитывать возвраты или использовать специальный финансовый календарь.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Потеря доверия:&lt;/b&gt; Когда пользователи получают противоречивые или неверные данные, доверие ко всей системе аналитики быстро падает.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Метрики не должны быть вероятностными, зависящими от “догадок” LLM при каждом вызове. &lt;b&gt;Они должны быть детерминированными.&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;`MetricFlow` решает именно эту задачу.&lt;/p&gt;
&lt;h3&gt;Что такое MetricFlow и как он работает&lt;/h3&gt;
&lt;p&gt;`MetricFlow` — это движок, который преобразует семантические определения бизнес-понятий в готовый к выполнению и оптимизированный SQL-код. Аналитик один раз определяет метрику “Валовая маржа” на языке `MetricFlow`, и после этого любая система (BI-инструмент, AI-агент, Python-скрипт) может запросить эту метрику по имени, будучи уверенной, что получит корректный и одинаковый результат.&lt;/p&gt;
&lt;h3&gt;Ключевые изменения и их значение&lt;/h3&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;b&gt;Лицензия Apache 2.0:&lt;/b&gt; Это одно из главных нововведений. Apache 2.0 — это разрешительная лицензия, которая позволяет другим компаниям свободно встраивать `MetricFlow` в свои коммерческие и открытые продукты. Это снимает барьеры для принятия технологии и способствует ее распространению как стандарта.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Сотрудничество с Open Semantic Interchange (OSI):&lt;/b&gt; dbt Labs будет развивать `MetricFlow` совместно с такими партнерами, как Snowflake и Salesforce, в рамках инициативы OSI. Цель — создать единый стандарт для семантической совместимости между разными платформами, чтобы метрики, определенные один раз, одинаково работали во всех инструментах.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Как MetricFlow обеспечивает надежность AI&lt;/h3&gt;
&lt;p&gt;`MetricFlow` предоставляет открытый стандарт для метаданных и расширяемый движок, который превращает намерение (“покажи валовую маржу”) в SQL-запрос для хранилища данных.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Пример работы:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Предположим, пользователь задает AI-агенту вопрос:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Покажи валовую маржу (%) по месяцам за прошлый квартал для Северной Америки (за вычетом скидок и возвратов, по финансовому календарю).”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Без семантического слоя LLM пришлось бы конструировать сложный запрос с нуля. С `MetricFlow` процесс выглядит так:&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Агент распознает намерение и запрашивает у `MetricFlow` метрику `gross_margin_pct` с нужными измерениями (`region`, `fiscal_month`) и фильтрами.&lt;/li&gt;
&lt;li&gt;`MetricFlow`, на основе заранее созданных определений, строит план запроса:
&lt;ul&gt;
  &lt;li&gt;Находит нужные таблицы: `orders`, `discounts`, `returns`, `cogs` (себестоимость).&lt;/li&gt;
  &lt;li&gt;Применяет правильные `JOIN` между ними.&lt;/li&gt;
  &lt;li&gt;Применяет фильтр по региону (`North America`).&lt;/li&gt;
  &lt;li&gt;Группирует данные по месяцам финансового, а не календарного, года.&lt;/li&gt;
  &lt;li&gt;Рассчитывает числитель (выручка) и знаменатель (себестоимость) с учетом того, что популяция данных для них должна быть одинаковой.&lt;/li&gt;
  &lt;li&gt;Вычисляет итоговое соотношение.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;`MetricFlow` компилирует этот план в оптимизированный SQL-запрос, специфичный для диалекта конкретного хранилища (Snowflake, BigQuery, Databricks и т.д.).&lt;/li&gt;
&lt;li&gt;Запрос выполняется в хранилище, и результат возвращается пользователю.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;При этом весь сгенерированный SQL доступен для проверки, что обеспечивает &lt;b&gt;прозрачность и объяснимость&lt;/b&gt; вычислений.&lt;/p&gt;
&lt;h3&gt;Основные возможности движка:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Единое определение, выполнение где угодно:&lt;/b&gt; Метрики и измерения определяются один раз, а `MetricFlow` компилирует их в SQL для разных диалектов.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Оптимизация производительности:&lt;/b&gt; Движок строит эффективные запросы, чтобы избежать лишних сканирований и снизить нагрузку на хранилище данных.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Поддержка сложных вычислений:&lt;/b&gt; `MetricFlow` из коробки обрабатывает сложные соединения, оконные функции, расчеты по когортам и полуаддитивные метрики (например, остатки на счетах, которые нельзя просто суммировать по времени).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;`MetricFlow` vs. `dbt Semantic Layer`&lt;/h3&gt;
&lt;p&gt;Важно понимать различие между двумя компонентами:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;`MetricFlow`&lt;/b&gt; — это движок с открытым исходным кодом для &lt;b&gt;определения и вычисления&lt;/b&gt; метрик. Это “сердце” системы, которое выполняет всю сложную работу по генерации SQL.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;`dbt Semantic Layer`&lt;/b&gt; — это коммерческий продукт dbt Labs, построенный *поверх* `MetricFlow`. Он добавляет функциональность корпоративного уровня:
&lt;ul&gt;
  &lt;li&gt;Управление доступом (`RBAC`).&lt;/li&gt;
  &lt;li&gt;Версионирование определений метрик.&lt;/li&gt;
  &lt;li&gt;Аудит и отслеживание происхождения данных (`lineage`).&lt;/li&gt;
  &lt;li&gt;Надежные API и коннекторы для интеграции с BI- и AI-инструментами.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Таким образом, `MetricFlow` становится общедоступным строительным блоком, а `dbt Semantic Layer` — готовым решением для его безопасного и управляемого внедрения в компаниях.&lt;/p&gt;
&lt;h3&gt;Итог&lt;/h3&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;b&gt;dbt Labs сделала `MetricFlow` (движок для расчета метрик) полностью открытым под лицензией Apache 2.0.&lt;/b&gt; Это позволяет всем желающим использовать его без ограничений.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Главная цель — создать открытый стандарт для определения бизнес-метрик.&lt;/b&gt; Это особенно актуально для AI-систем, которые часто ошибаются при самостоятельной генерации SQL.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;`MetricFlow` позволяет AI и BI-инструментам запрашивать данные по имени метрики (например, `revenue`), получая детерминированный и корректный SQL-запрос.&lt;/b&gt; Это повышает надежность и согласованность данных.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Этот шаг способствует совместимости инструментов (`interoperability`) и снижает зависимость от конкретного вендора (`vendor lock-in`).&lt;/b&gt; Метрики, определенные один раз, будут работать одинаково в разных системах.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Коммерческий продукт `dbt Semantic Layer` продолжит развиваться&lt;/b&gt; как решение для управления жизненным циклом метрик в корпоративной среде (безопасность, контроль версий, аудит).&lt;/li&gt;
&lt;/ol&gt;
</description>
</item>

<item>
<title>Новая эра трансформации данных: dbt против Bruin и aaC</title>
<guid isPermaLink="false">272</guid>
<link>https://gavrilov.info/all/novaya-era-transformacii-dannyh-dbt-protiv-bruin-i-aac/</link>
<pubDate>Sat, 23 Aug 2025 16:04:02 +0300</pubDate>
<author></author>
<comments>https://gavrilov.info/all/novaya-era-transformacii-dannyh-dbt-protiv-bruin-i-aac/</comments>
<description>
&lt;p&gt;В мире данных произошла тихая, но фундаментальная революция. На смену традиционному подходу &lt;b&gt;ETL (Extract, Transform, Load)&lt;/b&gt;, где данные преобразовывались до загрузки в хранилище, пришла новая парадигма — &lt;b&gt;ELT (Extract, Load, Transform)&lt;/b&gt;. Благодаря мощности современных облачных хранилищ (таких как Snowflake, BigQuery, Databricks, Starburst\Trino) стало выгоднее сначала загружать сырые данные, а уже затем трансформировать их непосредственно в хранилище.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/demo.gif" width="1200" height="900" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Этот сдвиг породил потребность в инструментах, которые специализируются на последнем шаге — трансформации (T). Именно в этой нише dbt (data build tool) стал безоговорочным лидером, но на его поле появляются и новые сильные игроки, такие как Bruin. Давайте разберемся, что это за инструменты, какой подход они олицетворяют и в чем их ключевые различия.&lt;/p&gt;
&lt;h4&gt;Подход «Аналитика как код»&lt;/h4&gt;
&lt;p&gt;И dbt, и Bruin являются яркими представителями движения &lt;b&gt;“Analytics as Code”&lt;/b&gt; (аналитика как код). Это не просто инструменты, а целая философия, которая переносит лучшие практики разработки программного обеспечения в мир аналитики данных.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Основные принципы и идеи:&lt;/b&gt;&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;b&gt;Версионирование:&lt;/b&gt; Все трансформации данных описываются в виде кода (в основном SQL), который хранится в системе контроля версий, такой как Git. Это позволяет отслеживать изменения, совместно работать и откатываться к предыдущим версиям.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Модульность и переиспользование (DRY – Don’t Repeat Yourself):&lt;/b&gt; Сложные трансформации разбиваются на небольшие, логически завершенные модели, которые могут ссылаться друг на друга. Это делает код чище, понятнее и позволяет повторно использовать уже написанную логику.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Тестирование:&lt;/b&gt; Код трансформаций должен быть протестирован. Инструменты позволяют автоматически проверять качество данных после преобразований: на уникальность ключей, отсутствие `NULL` значений, соответствие заданным условиям и т.д.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Документация и прозрачность:&lt;/b&gt; Процесс трансформации становится самодокументируемым. Инструменты могут автоматически генерировать документацию и строить графы зависимостей моделей (data lineage), показывая, как данные текут и преобразуются от источника к конечному виду. &lt;a href="https://www.element61.be/en/resource/when-use-dbt-or-delta-live-tables"&gt;element61.be&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CI/CD (Continuous Integration / Continuous Deployment):&lt;/b&gt; Изменения в коде трансформаций могут автоматически тестироваться и разворачиваться в продуктивную среду, что значительно ускоряет циклы разработки.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;b&gt;Решаемые проблемы:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;“Черные ящики” ETL:&lt;/b&gt; Заменяют сложные, трудноподдерживаемые и непрозрачные ETL-процессы на понятный и документированный код.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Рассинхронизация команд:&lt;/b&gt; Стирают границы между инженерами данных и аналитиками, позволяя аналитикам, владеющим SQL, самостоятельно создавать надежные модели данных.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Низкое качество данных:&lt;/b&gt; Встроенные механизмы тестирования помогают обеспечить надежность и согласованность данных.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;---&lt;/p&gt;
&lt;h4&gt;dbt (data build tool): Золотой стандарт трансформации&lt;/h4&gt;
&lt;p&gt;&lt;b&gt;dbt&lt;/b&gt; — это инструмент с открытым исходным кодом, который позволяет аналитикам и инженерам трансформировать данные в их хранилищах с помощью простых SQL-запросов. Важно понимать, что dbt &lt;b&gt;не извлекает и не загружает данные&lt;/b&gt;. Он специализируется исключительно на шаге &lt;b&gt;“T”&lt;/b&gt; в ELT  &lt;a href="https://vutr.substack.com/p/why-is-dbt-so-popular"&gt;vutr.substack.com&lt;/a&gt;. &lt;a href="https://github.com/dbt-labs/dbt-core"&gt;dbt git&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Он работает как компилятор и исполнитель: вы пишете модели данных в `.sql` файлах, используя шаблонизатор Jinja для добавления логики (циклы, условия, макросы). Затем dbt компилирует этот код в чистый SQL и выполняет его в вашем хранилище данных &lt;a href="https://www.element61.be/en/resource/when-use-dbt-or-delta-live-tables"&gt;element61.be&lt;/a&gt;.&lt;/p&gt;
&lt;h5&gt;Плюсы dbt&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Огромное сообщество и экосистема:&lt;/b&gt; dbt стал де-факто стандартом индустрии. Существует огромное количество статей, курсов, готовых пакетов (библиотек) и экспертов.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Фокус на SQL:&lt;/b&gt; Низкий порог входа для аналитиков, которые уже знают SQL. Это демократизирует процесс трансформации данных.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Мощное тестирование и документирование:&lt;/b&gt; Встроенные команды для тестирования данных и автоматической генерации проектной документации с графом зависимостей.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Зрелость и надежность:&lt;/b&gt; Инструмент проверен временем и используется тысячами компаний по всему миру.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Гибкость:&lt;/b&gt; Благодаря шаблонизатору Jinja можно создавать очень сложные и переиспользуемые макросы, адаптируя dbt под любые нужды.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Минусы dbt&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Только трансформация:&lt;/b&gt; dbt не занимается извлечением (E) и загрузкой (L). Для этого вам понадобятся отдельные инструменты (например, Fivetran, Airbyte), что усложняет стек технологий.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Кривая обучения:&lt;/b&gt; Хотя основы просты, освоение продвинутых возможностей Jinja, макросов и структуры проекта требует времени.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Зависимость от Python-моделей:&lt;/b&gt; Хотя недавно появилась поддержка моделей на Python, она все еще не так нативна и проста, как основной SQL-подход, и требует дополнительных настроек.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;---&lt;/p&gt;
&lt;h4&gt;Bruin Data: Универсальный боец&lt;/h4&gt;
&lt;p&gt;&lt;b&gt;Bruin&lt;/b&gt; — это более новый игрок на рынке, который позиционирует себя как инструмент для создания “end-to-end” пайплайнов данных. В отличие от dbt, он не ограничивается только трансформацией, а стремится охватить больше этапов работы с данными, включая их загрузку (ingestion) &lt;a href="https://github.com/bruin-data/bruin"&gt;https://github.com/bruin-data/bruin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Bruin разделяет ту же философию “Analytics as Code”, но предлагает более интегрированный опыт, где SQL и Python являются равноправными гражданами.&lt;/p&gt;
&lt;h5&gt;Плюсы Bruin&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Универсальность:&lt;/b&gt; Один инструмент для определения всего пайплайна: от загрузки из источников до финальных витрин данных. Это может упростить стек технологий.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Нативная поддержка SQL и Python:&lt;/b&gt; Позволяет легко комбинировать задачи на разных языках в одном пайплайне без дополнительных настроек. Это идеально для задач, где чистый SQL громоздок (например, работа с API, машинное обучение).&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Простота конфигурации:&lt;/b&gt; Зачастую требует меньше шаблонного кода (boilerplate) для определения ассетов и пайплайнов по сравнению с dbt.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Встроенное качество данных:&lt;/b&gt; Как и dbt, делает акцент на проверках качества на каждом шаге.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;Минусы Bruin&lt;/h5&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt; Пока маленькое сообщество:&lt;/b&gt; Как у нового инструмента, у Bruin гораздо меньше пользователей, готовых решений и обсуждений на форумах по сравнению с dbt. Найти помощь или готовый пакет для решения специфической задачи сложнее.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Незрелость:&lt;/b&gt; Инструмент моложе, а значит, наверное, потенциально менее стабилен и может иметь меньше интеграций по сравнению с проверенным dbt. Пока нет облачных функция за деньги. Я так думал, но все же есть &lt;a href="https://getbruin.com."&gt;https://getbruin.com.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;“Мастер на все руки — эксперт ни в чем?”:&lt;/b&gt; Стремление охватить все этапы (E, L, T) может означать, что в каждом отдельном компоненте Bruin может уступать лучшим в своем классе специализированным инструментам (например, Fivetran в загрузке, dbt в трансформации), но это конечно субъективно.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Сводное сравнение&lt;/h4&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;Характеристика&lt;/td&gt;
&lt;td style="text-align: center"&gt;dbt (data build tool)&lt;/td&gt;
&lt;td style="text-align: center"&gt;Bruin Data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Основная задача&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;Трансформация (T в ELT)&lt;/td&gt;
&lt;td style="text-align: center"&gt;Весь пайплайн (E, L, T)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Ключевые языки&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;SQL с шаблонизатором Jinja&lt;/td&gt;
&lt;td style="text-align: center"&gt;SQL и Python как равноправные&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Экосистема&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;Огромная, стандарт индустрии&lt;/td&gt;
&lt;td style="text-align: center"&gt;Маленькая, развивающаяся&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Зрелость&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;Высокая, проверен временем&lt;/td&gt;
&lt;td style="text-align: center"&gt;Низкая/Средняя&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Стек инструментов&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;Требует отдельных E/L инструментов&lt;/td&gt;
&lt;td style="text-align: center"&gt;Стремится быть самодостаточным&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;h4&gt;Итого&lt;/h4&gt;
&lt;p&gt;Выбор между dbt и Bruin — это выбор между двумя стратегиями построения современного стека данных.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Выбирайте dbt, если:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Вы строите гибкий стек из лучших в своем классе инструментов (“best-of-breed”): один для загрузки, другой для хранения, третий для трансформации.&lt;/li&gt;
&lt;li&gt;Ваша команда в основном состоит из аналитиков, сильных в SQL.&lt;/li&gt;
&lt;li&gt;Для вас критически важны поддержка сообщества, стабильность и наличие готовых решений.&lt;/li&gt;
&lt;li&gt;Вы работаете в большой организации, где принятие отраслевых стандартов является преимуществом.&lt;/li&gt;
&lt;li&gt;Вы готовы переехать к ним в платное облако, когда нибудь. Большая часть функционала доступна там.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;Выбирайте Bruin, если:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Вы предпочитаете единый, интегрированный инструмент для управления всеми пайплайнами, чтобы упростить архитектуру&lt;/li&gt;
&lt;li&gt;Вы любите open source и End-to-end дата framework: фор data ingestion + transformations + кволити. :)&lt;/li&gt;
&lt;li&gt;Ваши пайплайны требуют тесной связки SQL и Python для трансформаций (например, обогащение данных через вызовы API или модели ML).&lt;/li&gt;
&lt;li&gt;Вы начинаете новый проект или работаете в небольшой команде и цените скорость настройки и меньшее количество движущихся частей.&lt;/li&gt;
&lt;li&gt;Вы Go’шник :) –  Bruin написан на Go почти на 100%.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;И dbt, и Bruin — мощные инструменты, воплощающие современные подходы к инженерии данных. dbt предлагает проверенный, сфокусированный и невероятно мощный движок для трансформаций, ставший стандартом. Bruin же предлагает более универсальный и интегрированный подход, который может быть привлекателен для команд, стремящихся к простоте и нативной поддержке Python.&lt;/p&gt;
&lt;h4&gt;А что такое “Аналитика как код” (Analytics as Code, AaC)?&lt;/h4&gt;
&lt;p&gt;&lt;b&gt;Аналитика как код&lt;/b&gt; — это подход к управлению аналитическими процессами, при котором все компоненты аналитики — от моделей данных и метрик до отчетов и правил доступа — определяются в виде кода в человекочитаемых файлах. Эти файлы затем управляются так же, как исходный код любого другого программного обеспечения: с помощью систем контроля версий, автоматизированного тестирования и развертывания &lt;a href="https://medium.com/gooddata-developers/analytics-as-code-managing-analytics-solutions-like-any-other-software-504372ba6a61"&gt;medium.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Самая близкая и известная аналогия — это &lt;b&gt;Infrastructure as Code (IaC)&lt;/b&gt;. Как IaC (например, с помощью Terraform) позволил инженерам описывать серверы, сети и базы данных в коде вместо ручной настройки через веб-интерфейсы, так и AaC позволяет описывать в коде всё, что связано с данными &lt;a href="https://medium.com/@terezacihelkova/analytics-as-code-what-is-it-and-how-does-it-help-you-93e9a3c179ee"&gt;medium.com&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Идея проста и убедительна: “настройте свои системы один раз, выразите это в виде кода, а затем поместите в систему контроля версий” &lt;a href="https://www.holistics.io/blog/analytics-as-code/"&gt;holistics.io&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;Проблема: Как было раньше?&lt;/h4&gt;
&lt;p&gt;Чтобы понять ценность AaC, нужно посмотреть на проблемы, которые он решает. В традиционном подходе аналитика часто была разрозненной и хрупкой:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Логика в “черных ящиках”:&lt;/b&gt; Сложные преобразования данных были скрыты внутри GUI-интерфейсов старых ETL-инструментов или непосредственно в настройках BI-платформы (например, Tableau, Power BI). Никто, кроме автора, не мог легко понять, как рассчитывается та или иная метрика.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Разрозненные SQL-скрипты:&lt;/b&gt; Аналитики хранили важные SQL-запросы на своих локальных машинах, в общих папках или на wiki-страницах. Не было единой версии правды, код дублировался и быстро устаревал.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Отсутствие контроля версий:&lt;/b&gt; Невозможно было отследить, кто, когда и почему изменил логику расчета ключевого показателя. Откат к предыдущей работающей версии был настоящей головной болью.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;“Ручное” тестирование:&lt;/b&gt; Проверка качества данных после изменений была ручным, подверженным ошибкам процессом. Часто о проблемах узнавали уже от бизнес-пользователей, которые видели неверные цифры в отчетах.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Рассинхронизация:&lt;/b&gt; Инженеры данных готовили сырые таблицы, а аналитики строили свою логику поверх них. Любые изменения с одной стороны могли сломать всю цепочку, не будучи замеченными вовремя.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Этот хаос приводил к главному — &lt;b&gt;недоверию к данным&lt;/b&gt;. Никто не мог быть уверен, что цифры в дашборде верны.&lt;/p&gt;
&lt;h4&gt;Ключевые принципы “Аналитики как код”&lt;/h4&gt;
&lt;p&gt;AaC решает эти проблемы, внедряя практики из мира разработки ПО.&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;b&gt;Декларативное определение:&lt;/b&gt; Все аналитические артефакты описываются в файлах.
&lt;ul&gt;
  &lt;li&gt;Модели данных:** `SELECT * FROM ...` в `.sql` файлах.&lt;/li&gt;
  &lt;li&gt;Тесты:** `not_null`, `unique` в `.yml` файлах.&lt;/li&gt;
  &lt;li&gt;Документация:** Описания таблиц и полей в `.yml` файлах.&lt;/li&gt;
  &lt;li&gt;Метрики и дашборды:** Определения в `.yml` или специализированных файлах &lt;a href="https://medium.com/gooddata-developers/analytics-as-code-managing-analytics-solutions-like-any-other-software-504372ba6a61"&gt;medium.com&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol start="2"&gt;
&lt;li&gt;&lt;b&gt;Контроль версий (Git):&lt;/b&gt; Весь код хранится в репозитории (например, на GitHub или GitLab).
&lt;ul&gt;
  &lt;li&gt;Прозрачность:** Каждое изменение — это `commit` с понятным описанием.&lt;/li&gt;
  &lt;li&gt;Совместная работа:** Аналитики работают в отдельных ветках, а изменения вносятся через `Pull Request` (или `Merge Request`), что позволяет проводить ревью кода (code review).&lt;/li&gt;
  &lt;li&gt;Восстанавливаемость:** Если что-то пошло не так, можно легко откатиться к предыдущей версии.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol start="3"&gt;
&lt;li&gt;&lt;b&gt;Автоматизированное тестирование:&lt;/b&gt; Тесты являются неотъемлемой частью кода. Они запускаются автоматически при каждом изменении, чтобы гарантировать, что данные по-прежнему соответствуют ожиданиям (например, `user_id` всегда уникален и не равен `NULL`).&lt;/li&gt;
&lt;/ol&gt;
&lt;ol start="4"&gt;
&lt;li&gt;&lt;b&gt;CI/CD (Непрерывная интеграция и развертывание):&lt;/b&gt; Процессы полностью автоматизированы.
&lt;ul&gt;
  &lt;li&gt;Когда аналитик вносит изменения в `Pull Request`, автоматически запускаются тесты.&lt;/li&gt;
  &lt;li&gt;После одобрения и слияния ветки изменения автоматически развертываются в продуктивной среде (например, dbt Cloud или Jenkins запускает команду `dbt run`).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol start="5"&gt;
&lt;li&gt;&lt;b&gt;Модульность и переиспользование (DRY – Don’t Repeat Yourself):&lt;/b&gt; Сложные потоки данных разбиваются на небольшие, логичные и переиспользуемые модели. Одна модель может ссылаться на другую, создавая четкий граф зависимостей (lineage), который можно визуализировать.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Преимущества подхода AaC&lt;/h4&gt;
&lt;p&gt;Принятие этой философии дает компании ощутимые выгоды:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Надежность и доверие:&lt;/b&gt; Благодаря автоматическому тестированию и ревью кода значительно повышается качество данных, а вместе с ним и доверие бизнеса к аналитике.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Скорость и гибкость:&lt;/b&gt; Аналитики могут вносить изменения гораздо быстрее. Цикл от идеи до готового отчета сокращается с недель до дней или даже часов.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Масштабируемость:&lt;/b&gt; Кодовая база легко поддерживается и расширяется. Новые члены команды могут быстро разобраться в проекте благодаря документации и прозрачности.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Прозрачность и обнаруживаемость:&lt;/b&gt; Автоматически сгенерированная документация и графы зависимостей позволяют любому сотруднику понять, откуда берутся данные и как они рассчитываются.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Демократизация:&lt;/b&gt; AaC дает возможность аналитикам, владеющим SQL, самостоятельно создавать надежные и протестированные модели данных, не дожидаясь инженеров данных. Это стирает барьеры между командами.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;В конечном итоге, “Аналитика как код” — это культурный сдвиг, который превращает аналитику из ремесленного занятия в зрелую инженерную дисциплину, обеспечивая скорость, надежность и масштабируемость, необходимые современному бизнесу.&lt;/p&gt;
</description>
</item>

<item>
<title>Описание патерна Slowly Changing Dimensions (SCD)</title>
<guid isPermaLink="false">267</guid>
<link>https://gavrilov.info/all/opisanie-paterna-slowly-changing-dimensions-scd/</link>
<pubDate>Sat, 16 Aug 2025 23:24:59 +0300</pubDate>
<author></author>
<comments>https://gavrilov.info/all/opisanie-paterna-slowly-changing-dimensions-scd/</comments>
<description>
&lt;p&gt;&lt;b&gt;Slowly Changing Dimensions (SCD)&lt;/b&gt;, или &lt;b&gt;Медленно меняющиеся измерения&lt;/b&gt;, — это концепция и набор методов из области хранилищ данных (Data Warehousing), которые используются для управления изменениями в атрибутах измерений с течением времени. Измерения — это справочные таблицы, которые описывают бизнес-сущности, такие как клиенты, продукты, сотрудники, географические регионы.&lt;/p&gt;
&lt;p&gt;Атрибуты этих сущностей (например, адрес клиента или категория продукта) меняются, но обычно не очень часто — отсюда и название “медленно меняющиеся”. Основная задача SCD — решить, как хранить эти изменения, чтобы обеспечить точность исторических отчетов &lt;a href="https://www.datacamp.com/tutorial/mastering-slowly-changing-dimensions-scd"&gt;www.datacamp.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Например, если вы просто перезапишете адрес клиента, вы потеряете информацию о том, где он жил раньше. Это может исказить анализ продаж по регионам за прошлые периоды. Патерны SCD предлагают различные стратегии для решения этой проблемы.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://gavrilov.info/pictures/opisanie-paterna-slowly-changing-dimensions-scd.png" width="1060" height="720" alt="" /&gt;
&lt;/div&gt;
&lt;h4&gt;Основные типы SCD&lt;/h4&gt;
&lt;p&gt;Существует несколько типов SCD, но самыми распространенными и фундаментальными являются Типы 1, 2 и 3.&lt;/p&gt;
&lt;p&gt;---&lt;/p&gt;
&lt;h5&gt;Тип 1: Перезапись атрибута (Overwrite)&lt;/h5&gt;
&lt;p&gt;Это самый простой подход. При изменении атрибута старое значение просто перезаписывается новым.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Как работает:** Находится существующая запись в таблице измерения и значение в нужном столбце обновляется.&lt;/li&gt;
&lt;li&gt;Когда использовать:** Когда нет необходимости хранить историю изменений. Например, для исправления опечатки в имени клиента.&lt;/li&gt;
&lt;li&gt;Преимущества:** Простота реализации, не требует увеличения объема хранилища.&lt;/li&gt;
&lt;li&gt;Недостатки:&lt;b&gt; **История изменений полностью теряется.&lt;/b&gt; Анализ, основанный на исторических значениях атрибута, становится невозможным.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;Пример:&lt;/b&gt;&lt;br /&gt;
У нас есть клиент Анна Петрова, которая живет в Москве.&lt;/p&gt;
&lt;p&gt;*Таблица `DimCustomer` до изменений:*&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;CustomerKey&lt;/td&gt;
&lt;td style="text-align: center"&gt;FullName&lt;/td&gt;
&lt;td style="text-align: center"&gt;City&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;101&lt;/td&gt;
&lt;td style="text-align: center"&gt;Анна Петрова&lt;/td&gt;
&lt;td style="text-align: center"&gt;Москва&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Анна переезжает в Санкт-Петербург. При использовании &lt;b&gt;SCD Тип 1&lt;/b&gt; таблица будет обновлена:&lt;/p&gt;
&lt;p&gt;*Таблица `DimCustomer` после изменений:*&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;CustomerKey&lt;/td&gt;
&lt;td style="text-align: center"&gt;FullName&lt;/td&gt;
&lt;td style="text-align: center"&gt;City&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;101&lt;/td&gt;
&lt;td style="text-align: center"&gt;Анна Петрова&lt;/td&gt;
&lt;td style="text-align: right"&gt;Санкт-Петербург&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Теперь невозможно узнать, что раньше Анна жила в Москве.&lt;/p&gt;
&lt;p&gt;---&lt;/p&gt;
&lt;h5&gt;Тип 2: Добавление новой строки (Add New Row)&lt;/h5&gt;
&lt;p&gt;Это самый распространенный и мощный тип SCD, так как он позволяет сохранять полную историю изменений.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Как работает:** Вместо перезаписи существующей записи, создается новая запись для той же сущности (например, того же клиента). Старая запись помечается как неактуальная (истекшая), а новая — как актуальная. Для этого в таблицу измерения обычно добавляют несколько служебных столбцов &lt;a href="https://learn.microsoft.com/en-us/fabric/data-factory/slowly-changing-dimension-type-two"&gt;learn.microsoft.com&lt;/a&gt;:
&lt;ul&gt;
  &lt;li&gt;`StartDate` / `EffectiveDate` — дата, с которой запись стала актуальной.&lt;/li&gt;
  &lt;li&gt;`EndDate` — дата, когда запись перестала быть актуальной.&lt;/li&gt;
  &lt;li&gt;`IsCurrent` / `CurrentFlag` — флаг (например, ‘Yes’/’No’ или 1/0), показывающий, является ли эта запись текущей.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Когда использовать:** Когда сохранение истории критически важно для анализа. Это стандартный выбор для большинства атрибутов в хранилищах данных.&lt;/li&gt;
&lt;li&gt;Преимущества:** Сохраняется полная, точная история. Позволяет проводить корректный point-in-time анализ (анализ на определенный момент времени).&lt;/li&gt;
&lt;li&gt;Недостатки:** Увеличивается объем таблицы, так как для одного клиента может быть несколько записей. Запросы могут стать сложнее (нужно фильтровать по флагу `IsCurrent` или по диапазону дат) &lt;a href="https://hevodata.com/learn/slowly-changing-dimensions/"&gt;hevodata.com&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;Пример:&lt;/b&gt;&lt;br /&gt;
Снова используем пример с Анной Петровой.&lt;/p&gt;
&lt;p&gt;*Таблица `DimCustomer` до изменений:*&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;SurrogateKey&lt;/td&gt;
&lt;td style="text-align: center"&gt;CustomerID&lt;/td&gt;
&lt;td style="text-align: center"&gt;FullName&lt;/td&gt;
&lt;td style="text-align: center"&gt;City&lt;/td&gt;
&lt;td style="text-align: center"&gt;StartDate&lt;/td&gt;
&lt;td style="text-align: center"&gt;EndDate&lt;/td&gt;
&lt;td style="text-align: center"&gt;IsCurrent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;1&lt;/td&gt;
&lt;td style="text-align: center"&gt;101&lt;/td&gt;
&lt;td style="text-align: right"&gt;Анна Петрова&lt;/td&gt;
&lt;td style="text-align: center"&gt;Москва&lt;/td&gt;
&lt;td style="text-align: center"&gt;2020-01-15&lt;/td&gt;
&lt;td style="text-align: center"&gt;NULL&lt;/td&gt;
&lt;td style="text-align: center"&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Анна переезжает 16 августа 2024 года. При использовании &lt;b&gt;SCD Тип 2&lt;/b&gt; таблица изменится так:&lt;/p&gt;
&lt;p&gt;*Таблица `DimCustomer` после изменений:*&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;SurrogateKey&lt;/td&gt;
&lt;td style="text-align: center"&gt;CustomerID&lt;/td&gt;
&lt;td style="text-align: center"&gt;FullName&lt;/td&gt;
&lt;td style="text-align: center"&gt;City&lt;/td&gt;
&lt;td style="text-align: center"&gt;StartDate&lt;/td&gt;
&lt;td style="text-align: center"&gt;EndDate&lt;/td&gt;
&lt;td style="text-align: center"&gt;IsCurrent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;1&lt;/td&gt;
&lt;td style="text-align: center"&gt;101&lt;/td&gt;
&lt;td style="text-align: right"&gt;Анна Петрова&lt;/td&gt;
&lt;td style="text-align: center"&gt;Москва&lt;/td&gt;
&lt;td style="text-align: center"&gt;2020-01-15&lt;/td&gt;
&lt;td style="text-align: center"&gt;2024-08-15&lt;/td&gt;
&lt;td style="text-align: center"&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;101&lt;/td&gt;
&lt;td style="text-align: right"&gt;Анна Петрова&lt;/td&gt;
&lt;td style="text-align: right"&gt;Санкт-Петербург&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;2024-08-16&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;NULL&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Yes&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Теперь мы сохранили всю историю перемещений Анны.&lt;/p&gt;
&lt;p&gt;---&lt;/p&gt;
&lt;h5&gt;Тип 3: Добавление нового атрибута (Add New Attribute)&lt;/h5&gt;
&lt;p&gt;Этот тип сохраняет ограниченную историю, добавляя в таблицу отдельный столбец для предыдущего значения атрибута.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Как работает:** Создается новый столбец, например, `PreviousCity`. Когда атрибут `City` меняется, его старое значение копируется в `PreviousCity`, а новое записывается в `City`.&lt;/li&gt;
&lt;li&gt;Когда использовать:** Когда важно отслеживать только предыдущее состояние для сравнения, а более глубокая история не нужна.&lt;/li&gt;
&lt;li&gt;Преимущества:** Простота реализации, не увеличивает количество строк, легко запрашивать текущее и предыдущее значения.&lt;/li&gt;
&lt;li&gt;Недостатки:** Сохраняет историю только на один шаг назад. Не масштабируется, если нужно хранить более двух-трех последних значений.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;Пример:&lt;/b&gt;&lt;br /&gt;
Анна переезжает из Москвы в Санкт-Петербург.&lt;/p&gt;
&lt;p&gt;*Таблица `DimCustomer` до изменений:*&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;CustomerKey&lt;/td&gt;
&lt;td style="text-align: center"&gt;FullName&lt;/td&gt;
&lt;td style="text-align: center"&gt;CurrentCity&lt;/td&gt;
&lt;td style="text-align: center"&gt;PreviousCity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;101&lt;/td&gt;
&lt;td style="text-align: center"&gt;Анна Петрова&lt;/td&gt;
&lt;td style="text-align: center"&gt;Москва&lt;/td&gt;
&lt;td style="text-align: center"&gt;NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;*Таблица `DimCustomer` после изменений:*&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;CustomerKey&lt;/td&gt;
&lt;td style="text-align: center"&gt;FullName&lt;/td&gt;
&lt;td style="text-align: center"&gt;CurrentCity&lt;/td&gt;
&lt;td style="text-align: center"&gt;PreviousCity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;101&lt;/td&gt;
&lt;td style="text-align: center"&gt;Анна Петрова&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Санкт-Петербург&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Москва&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Если Анна переедет снова, значение “Москва” будет потеряно.&lt;/p&gt;
&lt;h4&gt;Другие типы SCD&lt;/h4&gt;
&lt;p&gt;Существуют и более сложные гибридные типы:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Тип 4 (History Table):** Основная таблица измерения хранит только текущие данные (как Тип 1), а вся история изменений выносится в отдельную таблицу. Это полезно, когда изменения происходят часто в очень больших таблицах измерений &lt;a href="https://medium.com/@zivilevalutytesilveira/slowly-changing-dimensions-in-data-warehousing-9479699bf40f"&gt;medium.com&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Тип 6 (Hybrid):** Комбинирует подходы Типов 1, 2 и 3. Например, в таблице хранятся поля для полной истории (SCD2) и одновременно поле для текущего значения (SCD1 для быстрого доступа) и предыдущего значения (SCD3 для сравнения).&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Тип 4: Добавление исторической таблицы (History Table / Audit Table)&lt;/h4&gt;
&lt;p&gt;&lt;b&gt;Идея:&lt;/b&gt; Разделить текущие данные и исторические данные в разные таблицы для оптимизации производительности.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Как работает:** Создаются две таблицы:
&lt;ol start="1"&gt;
  &lt;li&gt;&lt;b&gt;Таблица измерения (Dimension Table):&lt;/b&gt; Хранит *только* текущие, самые последние данные. Эта таблица по своей сути работает как &lt;b&gt;SCD Тип 1&lt;/b&gt; (данные просто перезаписываются). Она маленькая, быстрая и идеально подходит для большинства запросов, где история не нужна.&lt;/li&gt;
  &lt;li&gt;&lt;b&gt;Историческая таблица (History Table):&lt;/b&gt; Хранит всю историю изменений. Каждый раз, когда в основной таблице происходит изменение, старая версия строки (до обновления) добавляется в историческую таблицу. Эта таблица часто содержит служебные поля, как в SCD Тип 2 (`StartDate`, `EndDate`, `Version`), для отслеживания временного периода.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Когда использовать:** Когда у вас есть очень большая таблица измерений (например, десятки миллионов клиентов), и большинство аналитических запросов относится только к текущим данным. Разделение таблиц позволяет сделать эти частые запросы очень быстрыми, не жертвуя при этом возможностью проводить глубокий исторический анализ при необходимости.&lt;/li&gt;
&lt;li&gt;Преимущества:**
&lt;ul&gt;
  &lt;li&gt;Высокая производительность для запросов к текущим данным.&lt;/li&gt;
  &lt;li&gt;Логическое разделение данных: актуальные и исторические.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Недостатки:**
&lt;ul&gt;
  &lt;li&gt;Усложнение ETL/ELT процесса, так как нужно управлять двумя таблицами.&lt;/li&gt;
  &lt;li&gt;Анализ, требующий одновременного доступа к историческим и текущим данным, усложняется, так как требует `JOIN` или `UNION` между двумя таблицами.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;Пример:&lt;/b&gt;&lt;br /&gt;
Клиент Анна Петрова переезжает из Москвы в Санкт-Петербург.&lt;/p&gt;
&lt;p&gt;*Таблицы &lt;b&gt;до&lt;/b&gt; изменений:*&lt;/p&gt;
&lt;p&gt;`DimCustomer` (основная таблица)&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;CustomerID&lt;/td&gt;
&lt;td style="text-align: center"&gt;FullName&lt;/td&gt;
&lt;td style="text-align: center"&gt;City&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;101&lt;/td&gt;
&lt;td style="text-align: right"&gt;Анна Петрова&lt;/td&gt;
&lt;td style="text-align: center"&gt;Москва&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;`HistoryCustomer` (историческая таблица) – *пустая*&lt;/p&gt;
&lt;p&gt;*Процесс изменения:*&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;b&gt;Перед&lt;/b&gt; обновлением основной таблицы, текущая строка (Анна в Москве) копируется в `HistoryCustomer`.&lt;/li&gt;
&lt;li&gt;Затем основная таблица `DimCustomer` обновляется новым значением.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;*Таблицы &lt;b&gt;после&lt;/b&gt; изменений:*&lt;/p&gt;
&lt;p&gt;`DimCustomer` (всегда хранит только актуальные данные)&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;CustomerID&lt;/td&gt;
&lt;td style="text-align: center"&gt;FullName&lt;/td&gt;
&lt;td style="text-align: center"&gt;City&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;101&lt;/td&gt;
&lt;td style="text-align: right"&gt;Анна Петрова&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Санкт-Петербург&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;`HistoryCustomer` (накапливает историю)&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;HistoryID&lt;/td&gt;
&lt;td style="text-align: right"&gt;CustomerID&lt;/td&gt;
&lt;td style="text-align: center"&gt;FullName&lt;/td&gt;
&lt;td style="text-align: center"&gt;City&lt;/td&gt;
&lt;td style="text-align: center"&gt;StartDate&lt;/td&gt;
&lt;td style="text-align: center"&gt;EndDate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;1&lt;/td&gt;
&lt;td style="text-align: center"&gt;101&lt;/td&gt;
&lt;td&gt;Анна Петрова&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Москва&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;2020-01-15&lt;/td&gt;
&lt;td style="text-align: center"&gt;2024-08-15&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;h4&gt;Тип 5: Гибридный подход (Mini-Dimension + Type 1 Outrigger)&lt;/h4&gt;
&lt;p&gt;&lt;b&gt;Идея:&lt;/b&gt; Вынести часто меняющиеся атрибуты из большой таблицы измерений в отдельную “мини-таблицу”, чтобы избежать “раздувания” основной таблицы.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Как работает:**
&lt;ol start="1"&gt;
  &lt;li&gt;Из основной таблицы измерения (например, `DimCustomer`) выделяется группа атрибутов, которые часто меняются вместе (например, “Тарифный план”, “Статус подписки”).&lt;/li&gt;
  &lt;li&gt;Создается отдельная таблица — “мини-измерение” (например, `DimSubscriptionProfile`) — только для этих атрибутов. Эта мини-таблица управляется по &lt;b&gt;SCD Тип 2&lt;/b&gt; (добавление новой строки для каждого уникального набора значений).&lt;/li&gt;
  &lt;li&gt;В основной таблице `DimCustomer` эти атрибуты удаляются, и вместо них добавляется один внешний ключ (например, `SubscriptionProfileKey`), который ссылается на мини-измерение.&lt;/li&gt;
  &lt;li&gt;Этот ключ в основной таблице `DimCustomer` обновляется по принципу &lt;b&gt;SCD Тип 1&lt;/b&gt; (просто перезаписывается), указывая на *актуальную* запись в мини-измерении.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Когда использовать:** В очень больших (широких и/или с большим количеством строк) таблицах измерений, где лишь небольшая группа атрибутов меняется относительно часто. Это позволяет отслеживать историю этих атрибутов, не создавая новую многомиллионную запись в основной таблице при каждом изменении.&lt;/li&gt;
&lt;li&gt;Преимущества:**
&lt;ul&gt;
  &lt;li&gt;Экономия места и контроль над ростом основной таблицы измерения.&lt;/li&gt;
  &lt;li&gt;Позволяет вести детальную историю для подгруппы атрибутов.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Недостатки:**
&lt;ul&gt;
  &lt;li&gt;Более сложная модель данных, требующая дополнительных `JOIN`.&lt;/li&gt;
  &lt;li&gt;Может быть сложнее для понимания конечными пользователями.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;Пример:&lt;/b&gt;&lt;br /&gt;
Клиент Иван меняет свой тарифный план.&lt;/p&gt;
&lt;p&gt;*Таблицы &lt;b&gt;до&lt;/b&gt; изменений:*&lt;/p&gt;
&lt;p&gt;`DimCustomer`&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: right"&gt;CustomerKey&lt;/td&gt;
&lt;td style="text-align: center"&gt;FullName&lt;/td&gt;
&lt;td style="text-align: center"&gt;SubscriptionProfileKey&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;202&lt;/td&gt;
&lt;td style="text-align: center"&gt;Иван Иванов&lt;/td&gt;
&lt;td style="text-align: center"&gt;55&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;`DimSubscriptionProfile` (мини-измерение, управляется по SCD2)&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;ProfileKey&lt;/td&gt;
&lt;td style="text-align: center"&gt;Plan&lt;/td&gt;
&lt;td style="text-align: center"&gt;Status&lt;/td&gt;
&lt;td style="text-align: center"&gt;IsCurrent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;55&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;Basic&lt;/td&gt;
&lt;td style="text-align: center"&gt;Active&lt;/td&gt;
&lt;td style="text-align: center"&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;*Процесс изменения:* Иван переходит на план “Premium”.&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;В `DimSubscriptionProfile` добавляется новая строка для “Premium”, а старая помечается как неактуальная.&lt;/li&gt;
&lt;li&gt;В `DimCustomer` у Ивана обновляется ключ `SubscriptionProfileKey`.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;*Таблицы &lt;b&gt;после&lt;/b&gt; изменений:*&lt;/p&gt;
&lt;p&gt;`DimCustomer` (здесь изменился только ключ)&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: right"&gt;CustomerKey&lt;/td&gt;
&lt;td style="text-align: center"&gt;FullName&lt;/td&gt;
&lt;td style="text-align: center"&gt;SubscriptionProfileKey&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;202&lt;/td&gt;
&lt;td style="text-align: center"&gt;Иван Иванов&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;56&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;`DimSubscriptionProfile` (здесь хранится вся история)&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;ProfileKey&lt;/td&gt;
&lt;td style="text-align: center"&gt;Plan&lt;/td&gt;
&lt;td style="text-align: center"&gt;Status&lt;/td&gt;
&lt;td style="text-align: center"&gt;IsCurrent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;55&lt;/td&gt;
&lt;td style="text-align: center"&gt;Basic&lt;/td&gt;
&lt;td style="text-align: center"&gt;Active&lt;/td&gt;
&lt;td style="text-align: center"&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;56&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Premium&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Active&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Yes&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;h4&gt;Тип 6: Гибридный (Комбинация Типа 1, 2 и 3)&lt;/h4&gt;
&lt;p&gt;&lt;b&gt;Идея:&lt;/b&gt; Обеспечить максимальную гибкость для анализа, объединив сильные стороны трех основных типов в одной таблице.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Как работает:&lt;b&gt; Этот тип строится на основе **SCD Тип 2&lt;/b&gt; (добавление новой строки для истории), но с добавлением атрибутов из &lt;b&gt;SCD Тип 1&lt;/b&gt; (перезапись) для упрощения некоторых запросов.
&lt;ul&gt;
  &lt;li&gt;Основная структура — это SCD Тип 2: есть строки для каждой исторической версии с полями `StartDate`, `EndDate` и `IsCurrent`. Поле атрибута (например, `City`) хранит значение, актуальное на тот исторический период.&lt;/li&gt;
  &lt;li&gt;Дополнительно в таблицу добавляется столбец `CurrentCity`. Этот столбец для *всех* записей одного клиента (и исторических, и текущей) всегда хранит &lt;b&gt;актуальное на данный момент&lt;/b&gt; значение (поведение SCD Тип 1).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Когда использовать:** Когда аналитикам часто нужно отвечать на два типа вопросов:
&lt;ol start="1"&gt;
  &lt;li&gt;“Каким был город клиента на момент продажи?” (Используется историческое поле `City`).&lt;/li&gt;
  &lt;li&gt;“Каковы продажи всем клиентам, которые *сейчас* живут в Москве, за всю историю?” (Используется поле `CurrentCity` для фильтрации).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Преимущества:**
&lt;ul&gt;
  &lt;li&gt;Невероятная гибкость анализа без сложных `JOIN` или подзапросов для определения текущего состояния.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Недостатки:**
&lt;ul&gt;
  &lt;li&gt;Усложнение ETL/ELT. При изменении адреса нужно не только создать новую строку и закрыть старую, но и обновить поле `CurrentCity` во &lt;b&gt;всех&lt;/b&gt; предыдущих строках для этого клиента. Это может быть ресурсозатратно.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;Пример:&lt;/b&gt;&lt;br /&gt;
Снова Анна, переезжающая из Москвы в Санкт-Петербург.&lt;/p&gt;
&lt;p&gt;*Таблица `DimCustomer` &lt;b&gt;до&lt;/b&gt; изменений:*&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;SurrogateKey&lt;/td&gt;
&lt;td style="text-align: center"&gt;CustomerID&lt;/td&gt;
&lt;td style="text-align: center"&gt;City&lt;/td&gt;
&lt;td style="text-align: center"&gt;CurrentCity&lt;/td&gt;
&lt;td style="text-align: center"&gt;StartDate&lt;/td&gt;
&lt;td style="text-align: center"&gt;EndDate&lt;/td&gt;
&lt;td style="text-align: center"&gt;IsCurrent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;1&lt;/td&gt;
&lt;td style="text-align: center"&gt;101&lt;/td&gt;
&lt;td style="text-align: center"&gt;Москва&lt;/td&gt;
&lt;td style="text-align: center"&gt;Москва&lt;/td&gt;
&lt;td style="text-align: center"&gt;2020-01-15&lt;/td&gt;
&lt;td style="text-align: center"&gt;NULL&lt;/td&gt;
&lt;td style="text-align: center"&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;*Процесс изменения:*&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Старая строка “закрывается” (обновляется `EndDate`, `IsCurrent` = ‘No’).&lt;/li&gt;
&lt;li&gt;Создается новая актуальная строка.&lt;/li&gt;
&lt;li&gt;Во &lt;b&gt;всех строках&lt;/b&gt; для CustomerID=101 поле `CurrentCity` обновляется до “Санкт-Петербург”.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;*Таблица `DimCustomer` &lt;b&gt;после&lt;/b&gt; изменений:*&lt;/p&gt;
&lt;table cellpadding="0" cellspacing="0" border="0" class="e2-text-table"&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;SurrogateKey&lt;/td&gt;
&lt;td style="text-align: center"&gt;CustomerID&lt;/td&gt;
&lt;td style="text-align: center"&gt;City&lt;/td&gt;
&lt;td style="text-align: center"&gt;CurrentCity&lt;/td&gt;
&lt;td style="text-align: center"&gt;StartDate&lt;/td&gt;
&lt;td style="text-align: center"&gt;EndDate&lt;/td&gt;
&lt;td style="text-align: center"&gt;IsCurrent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;td style="text-align: left"&gt;:---&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;1&lt;/td&gt;
&lt;td style="text-align: center"&gt;101&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Москва&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Санкт-Петербург&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;2020-01-15&lt;/td&gt;
&lt;td style="text-align: center"&gt;2024-08-15&lt;/td&gt;
&lt;td style="text-align: center"&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: center"&gt;2&lt;/td&gt;
&lt;td style="text-align: center"&gt;101&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Санкт-Петербург&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;&lt;b&gt;Санкт-Петербург&lt;/b&gt;&lt;/td&gt;
&lt;td style="text-align: center"&gt;2024-08-16&lt;/td&gt;
&lt;td style="text-align: center"&gt;NULL&lt;/td&gt;
&lt;td style="text-align: center"&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Теперь можно легко отфильтровать по `City` для исторического анализа или по `CurrentCity` для анализа в разрезе текущего состояния.&lt;/p&gt;
&lt;h4&gt;Ссылки для дальнейшего изучения&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Microsoft Fabric:** Slowly changing dimension type 2 &lt;a href="https://learn.microsoft.com/en-us/fabric/data-factory/slowly-changing-dimension-type-two"&gt;https://learn.microsoft.com/en-us/fabric/data-factory/slowly-changing-dimension-type-two&lt;/a&gt; — Хорошее описание и пример реализации SCD Тип 2.&lt;/li&gt;
&lt;li&gt;DataCamp:** Mastering Slowly Changing Dimensions (SCD) &lt;a href="https://www.datacamp.com/tutorial/mastering-slowly-changing-dimensions-scd"&gt;https://www.datacamp.com/tutorial/mastering-slowly-changing-dimensions-scd&lt;/a&gt; — Комплексный учебник по основным типам SCD.&lt;/li&gt;
&lt;li&gt;HevoData:** Slowly Changing Dimensions(SCD): Types with Examples &lt;a href="https://hevodata.com/learn/slowly-changing-dimensions/"&gt;https://hevodata.com/learn/slowly-changing-dimensions/&lt;/a&gt; — Детальное объяснение всех основных типов с примерами.&lt;/li&gt;
&lt;li&gt;ThoughtSpot:** Slowly Changing Dimensions (SCD): 4 Types &amp; How to ...&lt;a href="https://www.thoughtspot.com/data-trends/data-modeling/slowly-changing-dimensions-in-data-warehouse"&gt;https://www.thoughtspot.com/data-trends/data-modeling/slowly-changing-dimensions-in-data-warehouse&lt;/a&gt; — Еще один ресурс с обзором и сравнением типов SCD.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Идея: Концептуальная архитектура: SCD на стеке Lakehouse + Data Mesh + dbt&lt;/p&gt;
&lt;p&gt;Основная идея заключается в создании надежных, версионируемых и децентрализованных “продуктов данных”, одним из которых является таблица измерений с полной историей (SCD). (Автоматическая)&lt;/p&gt;
&lt;p&gt;Вот как компоненты взаимодействуют друг с другом:&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;b&gt;Lakehouse (Основа):&lt;/b&gt; Это наша физическая среда. Мы используем открытое озеро данных (например, S3, ADLS) для хранения, а поверх него — табличный формат &lt;b&gt;Apache Iceberg&lt;/b&gt;. Iceberg предоставляет нам ACID-транзакции, эволюцию схемы и, что самое важное для SCD, атомарные и эффективные операции `MERGE` (`UPDATE`/`INSERT`/`DELETE`) на уровне строк прямо в озере данных.&lt;/li&gt;
&lt;/ol&gt;
&lt;ol start="2"&gt;
&lt;li&gt;&lt;b&gt;Data Mesh (Философия организации):&lt;/b&gt; Вместо централизованной команды данных, мы принимаем философию Data Mesh. A “Команда домена Клиенты” несет полную ответственность за все данные, связанные с клиентами. Их задача — предоставить остальной компании высококачественный продукт данных под названием `dim_customers`. Этот продукт должен включать полную историю изменений (SCD Type 2).&lt;/li&gt;
&lt;/ol&gt;
&lt;ol start="3"&gt;
&lt;li&gt;&lt;b&gt;ETL/ELT (Процесс):&lt;/b&gt; Это конвейер, по которому данные текут от источника к потребителю.
&lt;ul&gt;
  &lt;li&gt;Extract &amp; Load:&lt;b&gt; Исходные данные (например, изменения в базе данных клиентов) захватываются с помощью CDC (Change Data Capture) инструментов типа Debezium и попадают в **Kafka&lt;/b&gt;. Оттуда они загружаются (Load) в “бронзовый” слой нашего Lakehouse (в сыром виде, в таблицы Iceberg).&lt;/li&gt;
  &lt;li&gt;Transform:&lt;b&gt; Здесь в игру вступает **dbt&lt;/b&gt;. Команда домена использует `dbt` для преобразования сырых данных из бронзового слоя в готовую к использованию модель в “серебряном” слое — нашу таблицу `dim_customers`.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ol start="4"&gt;
&lt;li&gt;&lt;b&gt;dbt (Инструмент автоматизации SCD):&lt;/b&gt; `dbt` является сердцем автоматизации. Он не просто выполняет SQL-скрипты. У него есть встроенный функционал для реализации SCD Type 2, который называется &lt;b&gt;`Snapshots`&lt;/b&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;---&lt;/p&gt;
&lt;h4&gt;Сценарий 1: Автоматическое формирование SCD с помощью `dbt snapshots`&lt;/h4&gt;
&lt;p&gt;Это наиболее распространенный, надежный и идиоматический способ реализации идеи.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Как это работает:&lt;/b&gt;&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;b&gt;Источник:&lt;/b&gt; У нас есть “бронзовая” таблица `bronze_customers`, которая содержит текущее состояние всех клиентов. Эта таблица обновляется периодически (например, раз в час) новыми данными из Kafka.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;dbt Snapshot:&lt;/b&gt; В проекте `dbt` команда домена создает файл “снэпшота” (`snapshot/customers_snapshot.sql`). Внутри него описывается, как `dbt` должен отслеживать изменения.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;{% snapshot customers_snapshot %}

    {{
        config(
          target_schema='silver',
          unique_key='customer_id',
          strategy='check',
          check_cols=['address', 'email', 'phone_number'],
          updated_at='last_modified_at',
        )
    }}

    select * from {{ source('bronze', 'customers') }}

    {% endsnapshot %}&lt;/code&gt;&lt;/pre&gt;&lt;ol start="3"&gt;
&lt;li&gt;&lt;b&gt;Автоматизация:&lt;/b&gt; Оркестратор (например, Airflow) запускает команду `dbt snapshot` по расписанию.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Что делает dbt “под капотом”:&lt;/b&gt;
&lt;ul&gt;
  &lt;li&gt;Он сравнивает записи из исходной таблицы (`bronze_customers`) с текущими записями в целевой таблице (`silver.customers_snapshot`).&lt;/li&gt;
  &lt;li&gt;Используя `unique_key` (`customer_id`), он находит совпадающие записи.&lt;/li&gt;
  &lt;li&gt;С помощью стратегии `check` он проверяет, изменилось ли значение в любом из столбцов, перечисленных в `check_cols`.&lt;/li&gt;
  &lt;li&gt;Если изменение обнаружено:
&lt;ul&gt;
    &lt;li&gt;Он обновляет старую запись в целевой таблице, проставляя ей дату окончания актуальности (`dbt_valid_to`).&lt;/li&gt;
    &lt;li&gt;Он вставляет новую строку с обновленными данными и датой начала актуальности (`dbt_valid_from`).&lt;/li&gt;
  &lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;`dbt` генерирует одну атомарную операцию `MERGE` для таблицы Iceberg, которая эффективно выполняет все эти обновления и вставки за одну транзакцию.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;b&gt;Результат:&lt;/b&gt; В `silver.customers_snapshot` мы получаем идеальную таблицу SCD Type 2, которая обновляется автоматически и надежно, без написания сложной логики `MERGE` вручную.&lt;/p&gt;
</description>
</item>


</channel>
</rss>