# Почему моя модель пересоздаётся, а не используется повторно?

dbt State решает, можно ли повторно использовать модель, разбирая SQL-код, полученный после рендеринга, в синтаксическое дерево и сравнивая хеш. Если хеш изменился (что указывает на изменение логики модели), dbt State пересоздаёт модель.

Для dbt State безопасность и точность имеют приоритет: если нельзя гарантировать, что пропуск узла безопасен, узел пересоздаётся. На этой странице перечислены несколько распространённых ситуаций, которые приводят к избыточному пересозданию моделей, а также рекомендации по увеличению доли повторного использования.

Следующие ситуации часто приводят к неожиданному пересозданию моделей:

* [Представления с `select *`](#views-with-select)
* [Недетерминированные шаблоны Jinja](#non-deterministic-jinja-templating)
* [Модели с внешними источниками в BigQuery](#models-with-external-sources-in-bigquery)
* [Модели с пользовательскими материализациями](#models-with-custom-materializations)

## Представления с `select *`

dbt State повторно использует модель, когда хеш её скомпилированного SQL-кода совпадает с сохранённым хешем. Если представление использует `select *` непосредственно для `ref()` или `source()`, dbt не может определить список столбцов на этапе разбора — с момента последнего запуска в вышестоящей модели или исходной таблице могли появиться или исчезнуть столбцы. Чтобы избежать риска, dbt State принудительно пересоздаёт модель.

Например, это представление будет пересоздано, даже если `stg_orders` не изменилась, поскольку на этапе разбора dbt не может определить, остались ли в `stg_orders` те же столбцы, что и раньше:

```sql
-- stg_orders_view.sql (materialized: view)
select * from {{ ref('stg_orders') }}
```

Однако если использовать `select *` для CTE, dbt может определить столбцы из определения CTE и безопасно использовать представление повторно:

```sql
with renamed as (
    select order_id, customer_id, order_total from {{ ref('stg_orders') }}
)

select * from renamed
```

Если в CTE явно указаны имена столбцов, запрос `select *`, читающий из этого CTE, не приведёт к принудительному пересозданию, даже если в предыдущем CTE использовался `select *` для `ref()` или `source()`. Модель с типичной структурой слоя staging будет использоваться повторно:

```sql
with source as (
    select * from {{ source('jaffle_shop', 'orders') }}
),

renamed as (
    select
        id as order_id,
        user_id as customer_id,
        amount as order_total
    from source
)

select * from renamed
```

подсказка

Чтобы избежать принудительного пересоздания, явно указывайте имена столбцов при выборке непосредственно из `ref()` или `source()`. Также можно исключить представления из выполнения с помощью `--exclude config.materialized:view`.

## Недетерминированные шаблоны Jinja

Некоторые макросы и переменные окружения могут приводить к неожиданному пересозданию моделей. Например, `dbt_utils.get_relations_by_pattern` (макрос для анализа метаданных) в сочетании с `dbt_utils.union_relations` может возвращать объекты базы данных в разном порядке при каждом запуске, формируя разный SQL-код после рендеринга, даже если логика проекта не изменилась. Аналогично, переменные окружения, значения которых меняются между запусками, приводят к формированию разного SQL-кода после рендеринга при каждом запуске:

```sql
select '{{ env_var("AIRFLOW_RUN_ID") }}' as airflow_run_id, ...
```

Поскольку порядок результатов запроса или значение переменной окружения меняется, хеш SQL-кода после рендеринга отличается от сохранённого хеша при каждом запуске. dbt State считает это изменением кода и пересоздаёт модель, хотя основная логика проекта не изменилась. Эта ситуация может затронуть модели любого типа, а не только представления: если базовая модель или модель слоя staging пересоздаётся при каждом запуске, все зависящие от неё модели также пересоздаются.

Чтобы избежать этих ненужных пересозданий, включите [`compare_unrendered_code`](https://docs.getdbt.tech/reference/resource-configs/compare-unrendered-code.md). При включённом параметре dbt State проверяет и шаблон Jinja, и SQL-код после рендеринга; недетерминированные значения, которые не меняют шаблон, не вызывают пересоздания. Например:

```sql
{{ config(state={"compare_unrendered_code": true}) }}

select '{{ env_var("AIRFLOW_RUN_ID") }}' as airflow_run_id, ...
```

## Модели с внешними источниками в BigQuery

В BigQuery модели, использующие внешние источники (например, Google Sheets), всегда пересоздаются, поскольку BigQuery не предоставляет временные метки изменения внешних источников, и dbt State не может определить актуальность данных.

подсказка

Чтобы внешние источники не считались устаревшими при каждом запуске, настройте [`loaded_at_field`](https://docs.getdbt.tech/reference/resource-configs/freshness.md) или [`loaded_at_query`](https://docs.getdbt.tech/reference/resource-configs/freshness.md) в определении источника, указав поле с временной меткой. Это позволит dbt State запрашивать поле с временной меткой напрямую для определения актуальности данных, а не полагаться на метаданные хранилища.

## Модели с пользовательскими материализациями

Модели с пользовательскими материализациями всегда создаются заново и никогда не используются повторно. Пользовательские материализации могут иметь побочные эффекты (например, изменять свойства таблиц или записывать данные в другие схемы), и dbt State не может с уверенностью определить, приведёт ли пропуск выполнения к тому же результату.

## Как провести диагностику

После запуска используйте (Версии dbt: v2.0 и новее) [`dbt state explain`](https://docs.getdbt.tech/reference/commands/state-explain.md), чтобы узнать, почему dbt State пересоздал, повторно использовал или клонировал конкретную модель. Для подробного разбора используйте флаг `--verbose` вместе с `-s`, чтобы выбрать нужную модель:

примечание

Название команды зависит от версии: в dbt v2 используется `dbt state explain` (с пробелом), а в dbt v1 — `dbt-state explain` (с дефисом).

(Версии dbt: v2.0 и новее)

```bash
dbt state explain --verbose -s my_model_name
```

Если вы используете dbt platform, та же информация доступна без выполнения команды — перейдите на [вкладку **Explain**](https://docs.getdbt.tech/docs/deploy/dbt-state-interface.md#explain-tab) на странице сведений о запуске задания, чтобы увидеть полный разбор принятого решения для каждого узла.
