> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> Специальная документация по материализации materialized_view

# Materialized views

export const ClickHouseSupportedBadge = () => {
  return <div className="ClickHouseSupportedBadge">
            <div className="ClickHouseSupportedIcon">
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
                    <path d="M1.30762 1.39073C1.30762 1.3103 1.37465 1.22986 1.46849 1.22986H2.64824C2.72868 1.22986 2.80912 1.29689 2.80912 1.39073V14.4886C2.80912 14.5691 2.74209 14.6495 2.64824 14.6495H1.46849C1.38805 14.6495 1.30762 14.5825 1.30762 14.4886V1.39073Z" fill="currentColor" />
                    <path d="M4.2832 1.39073C4.2832 1.3103 4.35023 1.22986 4.44408 1.22986H5.62383C5.70427 1.22986 5.7847 1.29689 5.7847 1.39073V14.4886C5.7847 14.5691 5.71767 14.6495 5.62383 14.6495H4.44408C4.36364 14.6495 4.2832 14.5825 4.2832 14.4886V1.39073Z" fill="currentColor" />
                    <path d="M7.25977 1.39073C7.25977 1.3103 7.3268 1.22986 7.42064 1.22986H8.60039C8.68083 1.22986 8.76127 1.29689 8.76127 1.39073V14.4886C8.76127 14.5691 8.69423 14.6495 8.60039 14.6495H7.42064C7.3402 14.6495 7.25977 14.5825 7.25977 14.4886V1.39073Z" fill="currentColor" />
                    <path d="M10.2354 1.39073C10.2354 1.3103 10.3024 1.22986 10.3962 1.22986H11.576C11.6564 1.22986 11.7369 1.29689 11.7369 1.39073V14.4886C11.7369 14.5691 11.6698 14.6495 11.576 14.6495H10.3962C10.3158 14.6495 10.2354 14.5825 10.2354 14.4886V1.39073Z" fill="currentColor" />
                    <path d="M13.2256 6.6057C13.2256 6.52526 13.2926 6.44482 13.3865 6.44482H14.5662C14.6466 6.44482 14.7271 6.51186 14.7271 6.6057V9.27354C14.7271 9.35398 14.6601 9.43442 14.5662 9.43442H13.3865C13.306 9.43442 13.2256 9.36739 13.2256 9.27354V6.6057Z" fill="currentColor" />
                </svg>
            </div>
            Поддерживается в ClickHouse
        </div>;
};

<ClickHouseSupportedBadge />

Материализация `materialized_view` должна представлять собой `SELECT` из существующей исходной таблицы. В отличие от PostgreSQL, materialized view в ClickHouse не является «статическим» объектом (и для неё не существует соответствующей операции REFRESH). Вместо этого она работает как **триггер вставки**: новые строки вставляются в целевую таблицу за счёт применения заданного преобразования `SELECT` к строкам, вставляемым в исходную таблицу. Подробнее о том, как работают materialized view в ClickHouse, см. в [документации ClickHouse по materialized view](/docs/ru/concepts/features/materialized-views/index).

<Note>
  Общие сведения о концепциях материализации и общих конфигурациях (engine, order\_by, partition\_by и т. д.) см. на странице [Materializations](/docs/ru/integrations/connectors/data-ingestion/etl-tools/dbt/materializations).
</Note>

<div id="target-table-management">
  ## Как управляется целевая таблица
</div>

Когда вы используете материализацию `materialized_view`, dbt-clickhouse должен создать и **materialized view**, и **целевую таблицу**, в которую вставляются преобразованные строки. Управлять целевой таблицей можно двумя способами:

| Подход                      | Описание                                                                                                                                                                                                                                                                                                                                                                                                                             | Статус     |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- |
| **Неявная целевая таблица** | dbt-clickhouse автоматически создает целевую таблицу и управляет ею в рамках той же модели. Схема целевой таблицы выводится из SQL materialized view.                                                                                                                                                                                                                                                                                | Стабильный |
| **Явная целевая таблица**   | Вы определяете целевую таблицу как отдельную материализацию `table` и ссылаетесь на нее из модели materialized view с помощью макроса `materialization_target_table()`. Materialized view создается с предложением `TO`, указывающим на эту таблицу. Эта возможность доступна начиная с **dbt-clickhouse версии 1.10**. **Внимание**: эта возможность находится в статусе бета, и API может измениться на основе отзывов сообщества. | **Бета**   |

Выбранный подход влияет на то, как обрабатываются изменения схемы, полные обновления и конфигурации с несколькими materialized view. В следующих разделах каждый подход описан подробно.

<div id="implicit-target">
  ## Материализация с неявной целевой таблицей
</div>

Это поведение по умолчанию. Когда вы определяете модель `materialized_view`, адаптер:

1. Создаёт **целевую таблицу** с именем модели
2. Создаёт в ClickHouse **materialized view** с именем `<model_name>_mv`

Схема целевой таблицы выводится на основе столбцов в операторе `SELECT` materialized view. Все ресурсы (целевая таблица + materialized view) используют одну и ту же конфигурацию модели.

```sql theme={null}
-- models/events_mv.sql
{{
    config(
        materialized='materialized_view',
        engine='SummingMergeTree()',
        order_by='(event_date, event_type)'
    )
}}

SELECT
    toStartOfDay(event_time) AS event_date,
    event_type,
    count() AS total
FROM {{ source('raw', 'events') }}
GROUP BY event_date, event_type
```

См. [тестовый файл](https://github.com/ClickHouse/dbt-clickhouse/blob/main/tests/integration/adapter/materialized_view/test_materialized_view.py) с дополнительными примерами.

<Tip>
  Вы также можете задать для столбцов целевой таблицы `кодек` и `TTL`, если принудительно включите контракт модели. Подробнее см. в разделе [Конфигурация столбцов](/docs/ru/integrations/connectors/data-ingestion/etl-tools/dbt/materializations#column-configuration).
</Tip>

<div id="multiple-materialized-views">
  ### Несколько materialized view
</div>

ClickHouse позволяет нескольким materialized view записывать строки в одну и ту же целевую таблицу. Чтобы поддержать это в dbt-clickhouse при использовании подхода с неявной целевой таблицей, вы можете создать `UNION` в файле модели, заключив SQL для каждого materialized view между комментариями вида `--my_mv_name:begin` и `--my_mv_name:end`.

Например, следующий код создаст два materialized view, которые будут записывать данные в одну и ту же целевую таблицу модели. Имена materialized view будут иметь вид `<model_name>_mv1` и `<model_name>_mv2`:

```sql theme={null}
--mv1:begin
select a,b,c from {{ source('raw', 'table_1') }}
--mv1:end
union all
--mv2:begin
select a,b,c from {{ source('raw', 'table_2') }}
--mv2:end
```

<Warning>
  При обновлении model с несколькими materialized views (MV), особенно если вы переименовываете один из MV,
  dbt-clickhouse не удаляет старый MV автоматически. Вместо этого
  вы увидите следующее предупреждение:

  `Warning - Table <previous table name> was detected with the same pattern as model name <your model name> but was not found in this run. In case it is a renamed mv that was previously part of this model, drop it manually (!!!) `
</Warning>

<div id="how-to-iterate-the-target-table-schema">
  ### Как изменять схему целевой таблицы
</div>

Начиная с **dbt-clickhouse версии 1.9.8**, вы можете управлять тем, как изменяется схема целевой таблицы, когда `dbt run` обнаруживает различия в столбцах в SQL-коде materialized view.

```python theme={null}
{{config(
    materialized='materialized_view',
    engine='MergeTree()',
    order_by='(id)',
    on_schema_change='fail'  # этот параметр
)}}
```

По умолчанию dbt не применяет никаких изменений к целевой таблице (значение настройки — `ignore`), но вы можете изменить эту настройку, чтобы она вела себя так же, как config `on_schema_change` [в incremental-моделях](https://docs.getdbt.com/docs/build/incremental-models#what-if-the-columns-of-my-incremental-model-change).

Кроме того, эту настройку можно использовать как механизм защиты. Если задать для неё значение `fail`, сборка завершится ошибкой, если столбцы в SQL materialized view отличаются от столбцов в целевой таблице, созданной при первом `dbt run`.

<div id="data-catch-up">
  ### Догрузка данных
</div>

По умолчанию при создании или повторном создании materialized view (MV) целевая таблица сначала заполняется историческими данными, и только после этого создаётся сама materialized view (`catchup=True`). Это поведение можно отключить, установив параметру `catchup` значение `False`.

```python theme={null}
{{config(
    materialized='materialized_view',
    engine='MergeTree()',
    order_by='(id)',
    catchup=False  # этот параметр
)}}
```

| Операция                                     | `catchup: True` (по умолчанию)                               | `catchup: False`                                                       |
| -------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------- |
| Первоначальное развертывание (`dbt run`)     | В целевую таблицу выполняется дозагрузка исторических данных | Целевая таблица создается пустой                                       |
| Полное обновление (`dbt run --full-refresh`) | Целевая таблица пересоздается и дозагружается                | Целевая таблица пересоздается пустой, **существующие данные теряются** |
| Обычная работа                               | materialized view обрабатывает новые вставки                 | materialized view обрабатывает новые вставки                           |

<Warning>
  **Риск потери данных при полном обновлении**

  Использование `catchup: False` с `dbt run --full-refresh` **приведет к удалению всех существующих данных** в целевой таблице. Таблица будет пересоздана пустой и в дальнейшем будет обрабатывать только новые данные. Убедитесь, что у вас есть резервные копии, если исторические данные могут понадобиться позже.
</Warning>

<div id="explicit-target">
  ## Материализация с явной целевой таблицей (бета)
</div>

<Warning>
  **Бета**

  Эта возможность находится в статусе бета и доступна, начиная с **версии dbt-clickhouse 1.10**. API может измениться на основе отзывов сообщества.
</Warning>

По умолчанию dbt-clickhouse создает и управляет и целевой таблицей, и materialized view в рамках одной модели (подход с [неявной целевой таблицей](#implicit-target), описанный выше). У этого подхода есть ряд ограничений:

* Все ресурсы (целевая таблица + MV) используют одну и ту же конфигурацию. Если несколько MV указывают на одну и ту же целевую таблицу, их нужно определять вместе с помощью синтаксиса `UNION ALL`.
* Этими ресурсами нельзя управлять по отдельности — все они должны описываться в одном и том же файле модели.
* Нельзя легко задать имя для каждой MV.
* Все настройки общие для целевой таблицы и MV, поэтому сложно настраивать каждый ресурс отдельно и понимать, какая конфигурация к какому ресурсу относится.

Возможность **явной целевой таблицы** позволяет определить целевую таблицу отдельно как обычную материализацию `table`, а затем ссылаться на нее из моделей materialized view.

<div id="explicit-target-benefits">
  ### Преимущества
</div>

* **Полностью разделённые ресурсы**: Теперь каждый ресурс можно определять отдельно, что улучшает читаемость
* **Соответствие ресурсов 1:1 между dbt и CH**: Теперь вы можете использовать инструменты dbt, чтобы управлять ими и изменять их по отдельности.
* **Теперь доступны разные конфигурации**: Теперь к каждому из них можно применять отдельную конфигурацию.
* **Больше не нужно придерживаться соглашений об именовании**: Теперь все ресурсы создаются с использованием заданного вами имени, а не пользовательского имени с добавлением \_mv для MV.

<div id="explicit-target-limitations">
  ### Ограничения
</div>

* Определение целевой таблицы не вполне естественно для dbt: это не SQL-запрос, который читает из исходной таблицы, поэтому здесь вы лишаетесь проверок dbt. SQL materialized view по-прежнему будет проверяться с помощью утилит dbt, а его совместимость со столбцами целевой таблицы — на уровне CH.
* **Мы обнаружили несколько проблем, связанных с ограничениями функции `ref()`**: она нужна, чтобы ссылаться на модели друг из друга, но её можно использовать только для ссылок на вышестоящие модели, а не на нижестоящие. Это создаёт определённые сложности для данной реализации. Мы создали issue в репозитории dbt-core и сейчас обсуждаем с их командой [возможные решения (dbt-labs/dbt-core#12319)](https://github.com/dbt-labs/dbt-core/issues/12319):
  * Когда `ref()` вызывается внутри блока config, она возвращает текущую модель, а не ту, на которую должна ссылаться. Из-за этого мы не можем определить её в секции config() и вынуждены использовать комментарий, чтобы добавить эту зависимость. Мы придерживаемся того же подхода, что описан в документации dbt: [вариант с "--depends\_on:"](https://docs.getdbt.com/reference/dbt-jinja-functions/ref#forcing-dependencies).
  * `ref()` нам подходит, поскольку заставляет сначала создать целевую таблицу, но на графе зависимостей в сгенерированной документации целевая таблица будет показана как ещё одна вышестоящая зависимость, а не как нижестоящая, из-за чего это немного сложнее понять.
  * `unit-test` также вынуждает нас задавать данные для целевой таблицы, даже если по задумке чтения из неё быть не должно. Обходной путь здесь простой — оставить данные для этой таблицы пустыми.

<div id="explicit-target-usage">
  ### Использование
</div>

**Шаг 1: Определите целевую таблицу как обычную табличную модель**

Модель `events_daily.sql`:

```sql theme={null}
{{
    config(
        materialized='table',
        engine='SummingMergeTree()',
        order_by='(event_date, event_type)',
        partition_by='toYYYYMM(event_date)'
    )
}}

SELECT
    toDate(now()) AS event_date,
    '' AS event_type,
    toUInt64(0) AS total
WHERE 0  -- Создаёт пустую таблицу с правильной схемой
```

Это обходной путь, о котором мы упоминаем в разделе ограничений. Здесь вы можете потерять часть проверок dbt, но схема по-прежнему будет проверяться на уровне ClickHouse.

**Шаг 2: Определите materialized view, указывающие на целевую таблицу**

Например, можно определить разные MV в разных моделях, в том числе направив их в одну и ту же целевую таблицу. Обратите внимание на новый вызов макроса `{{ materialization_target_table(ref('events_daily')) }}`, который задаёт целевую таблицу для MV.

Модель `page_events_aggregator.sql`:

```sql theme={null}
{{ config(materialized='materialized_view') }}
{{ materialization_target_table(ref('events_daily')) }}

SELECT
    toStartOfDay(event_time) AS event_date,
    event_type,
    count() AS total
FROM {{ source('raw', 'page_events') }}
GROUP BY event_date, event_type
```

Модель `mobile_events_aggregator.sql`:

```sql theme={null}
{{ config(materialized='materialized_view') }}
{{ materialization_target_table(ref('events_daily')) }}

SELECT
    toStartOfDay(event_time) AS event_date,
    event_type,
    count() AS total
FROM {{ source('raw', 'mobile_events') }}
GROUP BY event_date, event_type
```

<div id="explicit-target-configuration">
  ### Параметры конфигурации
</div>

При использовании явных целевых таблиц, помимо [общих конфигураций материализации](/docs/ru/integrations/connectors/data-ingestion/etl-tools/dbt/materializations#general-materialization-configurations) и [конфигураций для таблиц](/docs/ru/integrations/connectors/data-ingestion/etl-tools/dbt/materializations#materialization-table), применяются следующие конфигурации:

**Для целевой таблицы (`materialized='table'`):**

| Параметр                              | Описание                                                                                                                                                                                                                                                                                   | По умолчанию                                                                                                                                                                                                                                                                                                              |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mv_on_schema_change`                 | Определяет, как обрабатывать изменения схемы, когда таблица используется MV, управляемыми dbt. Поведение соответствует конфигурации `on_schema_change` [в инкрементных моделях](https://docs.getdbt.com/docs/build/incremental-models#what-if-the-columns-of-my-incremental-model-change). | **Внимание**: модель `materialized='table'` будет вести себя как обычно, если на неё не ссылаются MV, поэтому даже если этот параметр задан, он будет проигнорирован. Если таблица является целевой для MV, значение этой конфигурации по умолчанию будет `mv_on_schema_change='fail'` для защиты данных в этих таблицах. |
| `repopulate_from_mvs_on_full_refresh` | При `--full-refresh` вместо выполнения SQL таблицы перестраивает её, выполняя INSERT-SELECT с использованием SQL всех MV, указывающих на неё.                                                                                                                                              | `False`                                                                                                                                                                                                                                                                                                                   |

**Для materialized view (`materialized='materialized_view'`):**

| Параметр  | Описание                                                             | По умолчанию |
| --------- | -------------------------------------------------------------------- | ------------ |
| `catchup` | Следует ли выполнять дозагрузку исторических данных при создании MV. | `True`       |

<Note>
  Обычно достаточно установить `catchup` в `True` для MV либо `repopulate_from_mvs_on_full_refresh` в `True` для их целевых таблиц. Если установить `True` для обоих параметров, это может привести к дублированию данных.
</Note>

<div id="explicit-target-common-operations">
  ### Основные операции
</div>

<div id="explicit-target-full-refresh">
  #### Полное обновление с явными целевыми таблицами
</div>

При использовании `--full-refresh` явные целевые таблицы будут пересозданы (поэтому вы можете потерять данные, если в ходе этого процесса продолжается ингестия). Поведение будет различаться в зависимости от конфигурации:

**Вариант 1: поведение `--full-refresh` по умолчанию. Всё будет пересоздано, но во время пересоздания MV целевая таблица будет пустой или загруженной лишь частично.**

Всё будет удалено и создано заново. Если вы хотите повторно выполнить вставку данных с помощью SQL для MV, оставьте параметр `catchup=True`:

```sql theme={null}
-- models/page_events_aggregator.sql
{{ config(
    materialized='materialized_view',
    catchup=True  -- это значение по умолчанию, поэтому его не нужно указывать явно.
) }}
{{ materialization_target_table(ref('events_daily')) }}
...
```

**Вариант 2: Я хочу заново создать целевую таблицу и не хочу, чтобы во время пересоздания MV читались пустые данные.**

Если вам сначала нужно обновить SQL у MV, вы можете задать в них `catchup=False`, а затем выполнить `dbt run` или `dbt run --full-refresh` для MV. Убедитесь, что MV созданы до запуска `--full-refresh` для целевой таблицы, так как при этом используются определения MV из ClickHouse.

Установите `repopulate_from_mvs_on_full_refresh=True` в модели целевой таблицы. При выполнении `dbt run --full-refresh` произойдет следующее:

1. Будет создана новая временная таблица
2. Будет выполнен INSERT-SELECT с использованием SQL каждой MV
3. Таблицы будут атомарно обменены местами

Таким образом, в вашей таблице не будет пустых данных, пока MV пересоздаются.

```sql theme={null}
-- models/events_daily.sql
{{
    config(
        materialized='table',
        engine='SummingMergeTree()',
        order_by='(event_date, event_type)',
        repopulate_from_mvs_on_full_refresh=True
    )
}}
...
```

<div id="explicit-target-changing">
  #### Изменение целевой таблицы
</div>

Вы не можете изменить целевую таблицу MV без `--full-refresh`. Если после изменения ссылки `materialization_target_table()` попробовать выполнить обычную команду `dbt run`, сборка завершится ошибкой с сообщением о том, что целевая таблица была изменена.

Чтобы изменить целевую таблицу:

1. Обновите вызов `materialization_target_table()`
2. Выполните `dbt run --full-refresh -s your_mv_model`

<div id="explicit-target-troubleshooting">
  ### Устранение типичных неполадок
</div>

<div id="target-table-empty">
  #### Целевая таблица пуста во время или после выполнения `run`
</div>

Это может происходить по нескольким причинам:

* Для materialized view может быть задано `catchup=False`, или для целевой таблицы может быть задано `repopulate_from_mvs_on_full_refresh=False`, поэтому при создании materialized views или пересоздании целевой таблицы дозагрузка не выполняется. Это ожидаемое поведение, поэтому, если вы хотите повторно вставить данные с помощью SQL materialized views, убедитесь, что для materialized view установлено `catchup=True` (это значение по умолчанию) или что для целевой таблицы задано `repopulate_from_mvs_on_full_refresh=True`. Не включайте оба параметра одновременно, чтобы избежать дубликатов. Подробнее см. в [разделе конфигурации](#explicit-target-configuration).
* Во время выполнения `dbt run --full-refresh`, если materialized views используют значение по умолчанию `catchup=True`, целевая таблица будет пересоздана, а MV последовательно повторно вставят данные. Чтобы избежать этой ситуации, см. раздел [Полное обновление с явными целевыми таблицами](#explicit-target-full-refresh).

<div id="full-refresh-with-repopulate-from-mvs-on-full-refresh">
  #### `dbt run --full-refresh` для целевой таблицы с `repopulate_from_mvs_on_full_refresh=True` использует логику из старых версий materialized view, а не из SQL, который сейчас находится в проекте
</div>

`repopulate_from_mvs_on_full_refresh=True` использует существующий SQL для MV, уже определённый в ClickHouse. Чтобы использовалось новое определение materialized view, выполните `dbt run` для каждой materialized view перед запуском `dbt run --full-refresh` для целевой таблицы.

<div id="duplicate-data">
  #### После выполнения запуска появляются дубликаты данных
</div>

Возможные причины:

* Одновременно могут быть включены и `catchup=True` для materialized view, и `repopulate_from_mvs_on_full_refresh=True` для целевой таблицы: оставьте только один из этих параметров в зависимости от того, какие операции вы хотите выполнять. Подробнее см. в [разделе конфигурации](#explicit-target-configuration).
* Целевая таблица определена без `WHERE 0`: целевая таблица должна создаваться пустой, но внутренний запрос может вставить данные, если не указано `WHERE 0`. Убедитесь, что это условие добавлено.

<div id="data-loss-active-ingestion">
  #### Потеря данных во время активной ингестии после выполнения `dbt run --full-refresh`
</div>

Некоторые строки из исходной таблицы отсутствуют в целевой таблице после выполнения `dbt run --full-refresh`.
materialized view в ClickHouse работают как триггеры вставки — они захватывают данные только пока существуют. Во время полного обновления возникает короткое окно, когда MV удаляется и создаётся заново («слепое окно»). Любые строки, вставленные в исходную таблицу в течение этого окна, не будут захвачены. Подробнее см. в разделе [Поведение во время активной ингестии](#behavior-during-active-ingestion).

<div id="debugging-techniques">
  ### Методы отладки
</div>

<div id="check-mv-target">
  #### Проверьте текущую целевую таблицу MV в ClickHouse
</div>

Выполните запрос к `system.tables`, чтобы посмотреть, в какую таблицу пишет materialized view:

```sql theme={null}
SELECT
    name as mv_name,
    replaceRegexpOne(
        create_table_query,
        '.*TO\\s+`?([^`\\s(]+)`?\\.`?([^`\\s(]+)`?.*',
        '\\1.\\2'
    ) AS target_table
FROM system.tables
WHERE database = 'your_schema'
  AND engine = 'MaterializedView'
```

<div id="check-dbt-recognition">
  #### Проверьте, распознаёт ли dbt таблицу как целевую таблицу materialized view
</div>

Во время запуска dbt найдите в журнале следующее сообщение:

> Таблица `<table_name>` используется как целевая таблица для materialized view, управляемого dbt. Чтобы предотвратить потерю данных, для mv\_on\_schema\_change по умолчанию устанавливается значение "fail".

Если это сообщение появляется, значит dbt обнаружил, что таблица служит целевой как минимум для одного materialized view, управляемого dbt. Если вы ожидаете увидеть это сообщение, но его нет, проверьте следующее:

* Модель materialized view корректно задаёт `{{ materialization_target_table(ref('your_target')) }}`
* В config модели materialized view задано `materialized='materialized_view'`
* И materialized view, и целевая таблица были выполнены как минимум один раз

<div id="migration-implicit-to-explicit">
  ### Переход от неявной к явной целевой таблице
</div>

Если у вас уже есть модели materialized view, использующие подход с неявной целевой таблицей, и вы хотите перейти на подход с явной целевой таблицей, выполните следующие шаги:

**1. Создайте модель целевой таблицы**

Создайте новый файл модели с `materialized='table'`, который определяет ту же схему, что и текущая целевая таблица MV. Используйте условие `WHERE 0`, чтобы создать пустую таблицу. Используйте то же имя, что и у текущей неявной модели materialized view. После этого вы сможете использовать эту модель для дальнейшей доработки целевой таблицы.

```sql theme={null}
-- models/events_daily.sql
{{
    config(
        materialized='table',
        engine='MergeTree()',
        order_by='(event_date, event_type)'
    )
}}

SELECT
    toDate(now()) AS event_date,
    '' AS event_type,
    toUInt64(0) AS total
WHERE 0
```

**2. Обновите модели MV**

Создайте новые модели, каждая из которых должна включать SQL-код MV и вызов macro `materialization_target_table()`, указывающий на новую целевую таблицу. Если ранее вы использовали `UNION ALL`, удалите эту часть и комментарии.

Для имён моделей нужно придерживаться следующего соглашения об именовании:

* если была определена только одна MV, она должна называться: `<old_model_name>_mv`
* если было определено несколько MV, каждая должна называться: `<old_model_name>_mv_<name_in_comments>`

Ранее в `my_model.sql` (неявная целевая таблица, одна модель с UNION ALL):

```sql theme={null}
--mv1:begin
select a, b, c from {{ source('raw', 'table_1') }}
--mv1:end
union all
--mv2:begin
select a, b, c from {{ source('raw', 'table_2') }}
--mv2:end
```

После (явная целевая таблица, отдельные файлы моделей):

```sql theme={null}
-- models/my_model_mv_mv1.sql
{{ config(materialized='materialized_view') }}
{{ materialization_target_table(ref('events_daily')) }}

select a, b, c from {{ source('raw', 'table_1') }}
```

```sql theme={null}
-- models/my_model_mv_mv2.sql
{{ config(materialized='materialized_view') }}
{{ materialization_target_table(ref('events_daily')) }}

select a, b, c from {{ source('raw', 'table_2') }}
```

**3. При необходимости повторяйте эти действия, следуя инструкциям в разделе [явная целевая таблица](#explicit-target).**

<div id="behavior-comparison">
  ## Сравнение поведения подходов с неявной и явной целевой таблицей
</div>

<div id="general-behavior">
  ### Общее поведение
</div>

| Операция               | Неявная целевая таблица                                                                                                                                                                                                                                                                                                                             | Явная целевая таблица                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Первый запуск dbt      | Создаются все ресурсы                                                                                                                                                                                                                                                                                                                               | Создаются все ресурсы                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| Следующий запуск dbt   | **Отдельно управлять ресурсами нельзя — все происходит вместе:**<br /><br />**целевая таблица**: <br /> изменения управляются настройкой `on_schema_change`. По умолчанию используется значение `ignore`, поэтому новые столбцы не обрабатываются.<br /><br />**Materialized views**: все обновляются с помощью операций `alter table modify query` | **Изменения можно применять по отдельности:<br /><br />целевая таблица**: <br />автоматически определяется, является ли таблица целевой таблицей для materialized views, объявленных в dbt. Если да, то изменение столбцов по умолчанию управляется настройкой `mv_on_schema_change` со значением `fail`, поэтому при изменении столбцов выполнение завершится ошибкой. Мы добавили это значение по умолчанию как дополнительный защитный механизм<br /><br />**Materialized views**: их SQL обновляется с помощью операций `alter table modify query`. |
| dbt run --full-refresh | **Отдельно управлять ресурсами нельзя — все происходит вместе:<br /><br />целевая таблица**: <br />целевая таблица пересоздается пустой. Доступен параметр `catchup` для настройки дозагрузки с SQL всех materialized views сразу. По умолчанию `catchup` имеет значение `True`<br /><br />**Materialized views**: все пересоздаются.               | **Изменения будут применяться по отдельности:<br /><br />целевая таблица:** будет пересоздана как обычно.<br /><br />**Materialized views**: удаляются и создаются заново. Для начальной дозагрузки доступен `catchup`. По умолчанию `catchup` имеет значение `True`. <br /><br />**Примечание: Во время этого процесса целевая таблица будет пустой или заполненной лишь частично, пока materialized views не будут пересозданы. Чтобы этого избежать, см. следующий раздел о том, как выполнять итерации целевой таблицы.**                           |

<div id="behavior-during-active-ingestion">
  ### Поведение во время активной ингестии
</div>

При итерации над моделями важно учитывать, как разные операции взаимодействуют с вставляемыми данными:

* Поскольку materialized view в ClickHouse работают как **триггеры вставки**, они захватывают данные только пока существуют. Если materialized view удаляется и создаётся заново (например, во время `--full-refresh`), любые строки, вставленные в исходную таблицу в этот промежуток, **не** будут обработаны materialized view. Это состояние называют «слепотой» materialized view.
* Все процессы `catchup` основаны на операциях `INSERT INTO ... SELECT`, использующих SQL materialized view, и не зависят от того, как работают сами materialized view. После запуска `INSERT` новые данные в него уже не попадают, но будут захвачены присоединённой materialized view.

В следующей таблице приведено, насколько безопасна каждая операция, когда в исходную таблицу активно выполняются вставки.

<div id="ingestion-implicit-target">
  #### Операции с неявной целевой таблицей
</div>

| Операция                 | Внутренний процесс                                                                                                                                                              | Безопасность во время вставок                                                                                                                                 |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Первый `dbt run`         | 1. Создание целевой таблицы<br />2. Вставка данных (если `catchup=True`)<br />3. Создание materialized view                                                                     | ⚠️ **materialized view не отслеживает данные между шагами 1 и 3.** Любые строки, вставленные в источник в этот промежуток, не будут захвачены.                |
| Последующий `dbt run`    | `ALTER TABLE ... MODIFY QUERY`                                                                                                                                                  | ✅ Безопасно. materialized view обновляется атомарно.                                                                                                          |
| `dbt run --full-refresh` | 1. Создание резервной таблицы<br />2. Вставка данных (если `catchup=True`)<br />3. Удаление materialized view<br />4. Обмен таблиц<br />5. Повторное создание materialized view | ⚠️ **materialized view не отслеживает данные во время пересоздания.** Данные, вставленные в источник между шагами 3 и 5, не появятся в новой целевой таблице. |

<div id="ingestion-explicit-target">
  #### Операции с явной целевой таблицей
</div>

**Модели materialized view:**

| Операция                        | Внутренний процесс                                                                      | Безопасность при выполняющихся вставках                                                                                                                                                                                                                                                                          |
| ------------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Первый `dbt run`                | 1. Создание MV (с секцией `TO`)<br />2. Выполнение дозагрузки (если `catchup=True`)     | ✅ MV создаётся первой, поэтому новые вставки начинают захватываться сразу.<br />⚠️ **Дозагрузка может привести к дублированию данных** — запрос дозагрузки может пересекаться со строками, которые MV уже обрабатывает. Это безопасно при использовании движка с дедупликацией (например, `ReplacingMergeTree`). |
| Последующий `dbt run`           | `ALTER TABLE ... MODIFY QUERY`                                                          | ✅ Безопасно. MV обновляется атомарно.                                                                                                                                                                                                                                                                            |
| `dbt run --full-refresh` для MV | 1. Удаление и повторное создание MV<br />2. Выполнение дозагрузки (если `catchup=True`) | ⚠️ **Во время пересоздания MV не видит новые данные** (между удалением и созданием).<br />⚠️ **Дозагрузка может привести к дублированию данных**, если вставки идут параллельно.                                                                                                                                 |

**Модель целевой таблицы:**

| Операция                                                              | Внутренний процесс                                                                                                    | Безопасность при выполняющихся вставках                                                                                                                             |
| --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `dbt run`                                                             | Изменения схемы применяются в соответствии с настройкой `mv_on_schema_change`                                         | ✅ Безопасно. Данные не перемещаются.                                                                                                                                |
| `dbt run --full-refresh` (по умолчанию)                               | Повторное создание таблицы (она остаётся пустой)                                                                      | ⚠️ **Целевая таблица пуста**, пока MV не дозаполнят её. Как только новая таблица появляется, MV продолжают вставку в неё.                                           |
| `dbt run --full-refresh` с `repopulate_from_mvs_on_full_refresh=True` | 1. Создание резервной таблицы<br />2. Вставка данных с использованием SQL каждой MV<br />3. Атомарный обмен таблицами | ⚠️ **Во время пересоздания MV не видит данные.** Данные, вставленные между шагами 1 и 3, не появятся в новой таблице. **Это может измениться в следующих версиях**. |

<Tip>
  **Рекомендации для продакшн-окружений с активной ингестией**

  * **По возможности приостанавливайте ингестию на время операций dbt**: так все операции будут безопасными, и данные не потеряются.
  * **По возможности используйте движок с дедупликацией** (например, `ReplacingMergeTree`) для целевой таблицы, чтобы обрабатывать возможные дубликаты из-за пересечений при дозагрузке.
  * **По возможности предпочитайте `ALTER TABLE ... MODIFY QUERY`** (обычный `dbt run` без `--full-refresh`) — это всегда безопасно.
  * **Учитывайте проблемные окна** во время операций dbt.
</Tip>

<div id="refreshable-materialized-views">
  ## Refreshable Materialized Views
</div>

[Refreshable Materialized Views](/docs/ru/concepts/features/materialized-views/refreshable-materialized-view) — это особый тип materialized view в ClickHouse, который периодически заново выполняет запрос и сохраняет результат — аналогично materialized view в других базах данных. Это полезно в сценариях, где нужны периодические снимки или агрегации, а не триггеры вставки в реальном времени.

<Tip>
  Refreshable materialized view можно использовать **как** с подходом [неявная целевая таблица](#implicit-target), **так и** с подходом [явная целевая таблица](#explicit-target). Конфигурация `refreshable` не зависит от того, как управляется целевая таблица.
</Tip>

Чтобы использовать refreshable materialized view, добавьте объект конфигурации `refreshable` в модель MV со следующими параметрами:

| Параметр                | Описание                                                                                                                                                                         | Обязательно | Значение по умолчанию |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | --------------------- |
| refresh\_interval       | Предложение interval (обязательно)                                                                                                                                               | Да          |                       |
| randomize               | Предложение рандомизации; указывается после `RANDOMIZE FOR`                                                                                                                      |             |                       |
| append                  | Если задано значение `True`, при каждом обновлении строки добавляются в таблицу без удаления существующих строк. Эта вставка не является атомарной, как и обычный INSERT SELECT. |             | False                 |
| depends\_on             | Список зависимостей для refreshable MV. Указывайте зависимости в следующем формате: `{schema}.{view_name}`                                                                       |             |                       |
| depends\_on\_validation | Определяет, нужно ли проверять существование зависимостей, указанных в `depends_on`. Если для зависимости не указана схема, проверка выполняется в схеме `default`               |             | False                 |

<div id="refreshable-implicit-example">
  ### Пример с неявной целевой таблицей
</div>

```python theme={null}
{{
    config(
        materialized='materialized_view',
        engine='MergeTree()',
        order_by='(event_date)',
        refreshable={
            "interval": "EVERY 5 MINUTE",
            "randomize": "1 MINUTE",
            "append": True,
            "depends_on": ['schema.depend_on_model'],
            "depends_on_validation": True
        }
    )
}}

SELECT
    toStartOfDay(event_time) AS event_date,
    count() AS total
FROM {{ source('raw', 'events') }}
GROUP BY event_date
```

<div id="refreshable-explicit-example">
  ### Пример с явной целевой таблицей
</div>

```python theme={null}
{{
    config(
        materialized='materialized_view',
        refreshable={
            "interval": "EVERY 1 HOUR",
            "append": False
        }
    )
}}
{{ materialization_target_table(ref('events_daily')) }}

SELECT
    toStartOfDay(event_time) AS event_date,
    event_type,
    count() AS total
FROM {{ source('raw', 'events') }}
GROUP BY event_date, event_type
```

<div id="explicit-target-limitations">
  ### Ограничения
</div>

* При создании в ClickHouse refreshable materialized view (MV) с зависимостью ClickHouse не генерирует
  ошибку, если указанная зависимость не существует на момент создания. Вместо этого refreshable MV остается в
  неактивном состоянии и ожидает, пока зависимость не будет удовлетворена, прежде чем начнет обрабатывать обновления или обновляться.
  Такое поведение является штатным, но оно может приводить к задержкам в доступности данных, если требуемая
  зависимость не будет своевременно создана. Перед созданием refreshable
  materialized view следует убедиться, что все зависимости корректно определены и существуют.
* На данный момент фактическая «связь dbt» между mv и его зависимостями отсутствует, поэтому порядок создания не
  гарантируется.
* Возможность refreshable не тестировалась для нескольких mv, направляющих данные в одну и ту же целевую модель.
