dbt (data build tool) превращает SQL-трансформации в хранилище в проект с зависимостями, тестами, документацией и CI. Это буква T в ELT: данные уже загружены, dbt строит из них модели. В вакансиях дата-инженеров и аналитиков-инженеров в 2026 году dbt встречается постоянно.
Модель
Модель это файл .sql с одним SELECT. dbt сам оборачивает его в CREATE TABLE AS или CREATE VIEW AS.
-- models/marts/fct_orders.sql
select
o.order_id,
o.customer_id,
o.created_at::date as order_date,
sum(i.amount) as amount
from {{ ref('stg_orders') }} o
join {{ ref('stg_order_items') }} i using (order_id)
group by 1, 2, 3
{{ ref('модель') }}ссылается на другую модель. dbt по ссылкам строит граф зависимостей (DAG), запускает модели в правильном порядке и подставляет правильную схему окружения (dev, prod).{{ source('схема', 'таблица') }}ссылается на сырые таблицы, загруженные извне. Для источников можно описать проверку свежести.- Jinja позволяет писать макросы, циклы и условия, но злоупотреблять ими не стоит: SQL должен оставаться читаемым.
Материализации
- view: представление, всегда свежее, но считается при каждом запросе.
- table: таблица пересоздаётся целиком при каждом запуске.
- incremental: при первом запуске таблица создаётся, дальше обрабатываются только новые или изменённые строки.
- ephemeral: модель не создаётся в базе, а встраивается как CTE в зависящие модели.
- Ещё бывают материализованные представления, если хранилище их поддерживает.
Инкрементальные модели
{{ config(materialized='incremental', unique_key='order_id') }}
select order_id, status, amount, updated_at
from {{ ref('stg_orders') }}
{% if is_incremental() %}
where updated_at > (select max(updated_at) from {{ this }})
{% endif %}
is_incremental()истинно, когда таблица уже существует и запуск не полный.{{ this }}ссылается на текущую модель в базе.unique_keyопределяет, как вставлять: при совпадении ключа строка обновляется (merge или delete+insert в зависимости от адаптера и стратегии), иначе дописывается.- Опоздавшие данные: фильтр
> max(updated_at)пропустит строки, пришедшие с опозданием и меткой времени раньше максимума. Часто берут окно с запасом:>= max(updated_at) - interval '3 days', а дубли гаситunique_key. dbt run --full-refreshпересобирает модель целиком, например после изменения логики.
Тесты
Data tests (так их называют с dbt 1.8) проверяют данные в базе. Встроенные generic-тесты в YAML:
models:
- name: fct_orders
columns:
- name: order_id
data_tests: [unique, not_null]
- name: customer_id
data_tests:
- relationships:
to: ref('dim_customers')
field: customer_id
- name: status
data_tests:
- accepted_values:
values: ['new', 'paid', 'cancelled']
Тест это запрос, который ищет плохие строки: если он вернул строки, тест упал. Можно писать свои singular-тесты обычным SQL и подключать пакеты (например, dbt-utils, dbt-expectations). С dbt 1.8 появились также unit tests: проверка логики модели на заданных входных строках без реальных данных.
dbt build запускает модели, тесты, снапшоты и сиды в порядке графа: если тест модели упал, зависимые модели не строятся.
Снапшоты
Snapshot реализует SCD 2 для изменяемой таблицы источника: dbt хранит версии строк с колонками dbt_valid_from и dbt_valid_to. Стратегии:
timestamp: изменение определяется по колонке времени обновления.check: сравниваются значения перечисленных колонок.
Снапшоты запускают регулярно: изменения между запусками не будут видны, если строка успела поменяться дважды.
Слои проекта
Типичная структура:
- staging: одна модель на таблицу источника, переименование, приведение типов, без джойнов. Обычно view.
- intermediate: промежуточная логика, джойны и подготовка.
- marts: факты и измерения для потребителей, таблицы или инкрементальные модели.
Плюс сиды (seeds, небольшие CSV-справочники в репозитории), макросы, документация (dbt docs с графом lineage), окружения через profiles и target.
dbt в процессе
- Проект в git, изменения через merge request.
- CI собирает изменённые модели и их потомков (
state:modified+) во временной схеме и прогоняет тесты. - Продакшн-запуск по расписанию из оркестратора (Airflow, Dagster) или dbt Cloud.
Частые ошибки
- Хардкод имён таблиц вместо
refиsource: ломается граф и окружения. - Инкрементальная модель без
unique_keyи без учёта опозданий. - Вся логика в одной модели на 500 строк вместо слоёв.
- Тесты только на
uniqueиnot_nullу ключа и больше ничего. - Jinja ради Jinja: макросы, которые никто не может прочитать.
Что спросят на собесе
- Что такое модель,
refиsource, как dbt строит граф. - Материализации и когда какую выбирать.
- Как работает инкрементальная модель и что делать с опозданиями.
- Какие тесты бывают, что делает
dbt build. - Снапшоты и их связь с SCD 2.