contract
Поддерживается в dbt версии 1.5 и выше.
Когда конфигурация contract
применяется, dbt будет гарантировать, что возвращаемый набор данных вашей модели точно соответствует атрибутам, которые вы определили в yaml:
name
иdata_type
для каждого столбца- Дополнительные
ограничения
, как поддерживается для этой материализации и платформы данных
Это необходимо для того, чтобы люди, выполняющие запросы к вашей модели далее по цепочке — как внутри, так и вне dbt — имели предсказуемый и согласованный набор столбцов для использован ия в своих анализах. Даже незначительное изменение типа данных, например, из boolean
(true
/false
) в integer
(0
/1
), может привести к неожиданным сбоям запросов.
Поддержка
В настоящее время контракты моделей поддерживаются для:
- SQL моделей (пока не для Python)
- Моделей, материализованных как
table
,view
иincremental
(сon_schema_change: append_new_columns
илиon_schema_change: fail
) - Наиболее популярных платформ данных — хотя поддержка и применение различных типов ограничений варьируются в зависимости от платформы
Алиасинг типов данных
dbt использует встроенный алиасинг типов для data_type
, определенного в вашем YAML. Например, вы можете указать string
в вашем контракте, и на Postgres/Redshift dbt преобразует его в text
. Если dbt не распознает имя data_type
среди известных алиасов, он передаст его как есть. Это включено по умолчанию, но вы можете отказаться, установив alias_types
в false
.
Пример отключения:
models:
- name: my_model
config:
contract:
enforced: true
alias_types: false # true по умолчанию
Размер, точность и масштаб
Когда dbt сравнивает типы данных, он не будет сравнивать такие детальные характеристики, как размер, точность или масштаб. Мы считаем, что вам не стоит беспокоиться о разнице между varchar(256)
и varchar(257)
, так как это не сильно влияет на опыт пользователей, выполняющих запросы далее по цепочке. Вы можете добиться более точного утверждения, написав или используя пользовательский тест.
Обратите внимание, что вам нужно указать размер varchar или масштаб числового типа, иначе dbt полагается на значения по умолчанию. Например, если тип numeric
по умолчанию имеет точность 38 и масштаб 0, то числовой столбец хранит 0 цифр справа от десятичной точки (он хранит только целые числа), что может привести к сбою применения контракта. Чтобы избежать этого неявного приведения, укажите ваш data_type
с ненулевым масштабом, например, numeric(38, 6)
. dbt Core 1.7 и выше выдает предупреждение, если вы не указываете точность и масштаб при предоставлении числового типа данных.
Пример
models:
- name: dim_customers
config:
materialized: table
contract:
enforced: true
columns:
- name: customer_id
data_type: int
constraints:
- type: not_null
- name: customer_name
data_type: string
- name: non_integer
data_type: numeric(38,3)
Предположим, ваша модель определена как:
select
'abc123' as customer_id,
'My Best Customer' as customer_name
Когда вы выполняете dbt run
для вашей модели, до того, как dbt материализует ее как таблицу в базе данных, вы увидите эту ошибку:
20:53:45 Ошибка компиляции в модели dim_customers (models/dim_customers.sql)
20:53:45 Эта модель имеет применяемый контракт, который не выполнен.
20:53:45 Пожалуйста, убедитесь, что имя, data_type и количество столбцов в вашем контракте соответствуют столбцам в определении вашей модели.
20:53:45
20:53:45 | column_name | definition_type | contract_type | mismatch_reason |
20:53:45 | ----------- | --------------- | ------------- | ------------------ |
20:53:45 | customer_id | TEXT | INT | несоответствие типа данных |
20:53:45
20:53:45
20:53:45 > в макросе assert_columns_equivalent (macros/materializations/models/table/columns_spec_ddl.sql)
Инкрементальные модели и on_schema_change
Почему требуется, чтобы инкрементальные модели также устанавливали on_schema_change
, и почему append_new_columns
?
Представьте:
- Вы добавляете новый столбец как в SQL, так и в спецификацию YAML
- Вы не устанавливаете
on_schema_change
, или устанавливаетеon_schema_change: 'ignore'
- dbt фактически не добавляет этот новый столбец в существующую таблицу — и обновление/слияние все равно успешно, потому что оно выполняется на основе уже существующих "целевых" столбцов (это давно установленное поведение)
- В результате возникает расхождение между контрактом, определенным в yaml, и фактической таблицей в базе данных — что означает, что контракт теперь неверен!
Почему append_new_columns
, а не sync_all_columns
? Потому что удаление существующих столбцов является критическим изменением для моделей с контрактами!