Модели и материализации¶
Модель в dbt — это SQL-файл с одним SELECT. dbt превращает этот SELECT в объект в базе данных: представление, таблицу или инкрементальную таблицу. Способ превращения называется материализацией.
Четыре типа материализаций¶
view (по умолчанию)¶
dbt создаёт CREATE VIEW. Данные не хранятся — каждый запрос выполняет SELECT заново.
-- models/staging/stg_users.sql
-- Материализация: view (по умолчанию)
SELECT
id AS user_id,
email,
created_at
FROM {{ source('raw', 'users') }}
WHERE is_deleted = false
Когда использовать:
- Лёгкие трансформации (переименование, фильтрация)
- Staging-слой — данные меняются часто, объём небольшой
- Не нужна предварительная агрегация
table¶
dbt создаёт CREATE TABLE AS SELECT. Данные материализуются физически — запрос выполняется один раз при сборке.
-- models/marts/dim_products.sql
{{ config(materialized='table') }}
SELECT
p.product_id,
p.name,
p.sku,
c.category_name,
p.price,
p.created_at
FROM {{ ref('stg_products') }} p
LEFT JOIN {{ ref('stg_categories') }} c
ON p.category_id = c.category_id
Когда использовать:
- Витрины (marts), к которым обращаются аналитики
- Тяжёлые агрегации и JOIN
- Данные, которые полностью пересоздаются при каждом запуске
table пересоздаёт данные целиком
При каждом dbt run таблица удаляется и создаётся заново. Для больших таблиц это может быть медленно — используй incremental.
incremental¶
dbt добавляет только новые или изменённые строки. При первом запуске создаёт таблицу целиком, при последующих — вставляет дельту.
-- models/marts/fct_events.sql
{{ config(
materialized='incremental',
unique_key='event_id'
) }}
SELECT
event_id,
user_id,
event_type,
created_at
FROM {{ source('raw', 'events') }}
{% if is_incremental() %}
WHERE created_at > (SELECT MAX(created_at) FROM {{ this }})
{% endif %}
Ключевые элементы:
| Элемент | Назначение |
|---|---|
materialized='incremental' |
Включает инкрементальный режим |
unique_key='event_id' |
Ключ для обновления существующих строк (UPSERT) |
is_incremental() |
Возвращает true при повторных запусках |
{{ this }} |
Ссылка на саму таблицу (для фильтрации по последней дате) |
Когда пересоздать с нуля
Если изменилась логика модели, запусти dbt run --full-refresh — dbt пересоздаст таблицу целиком, игнорируя инкрементальную логику.
ephemeral¶
dbt не создаёт ни таблицы, ни представления. Модель превращается в CTE, который подставляется в зависимые модели.
-- models/intermediate/int_order_amounts.sql
{{ config(materialized='ephemeral') }}
SELECT
order_id,
SUM(qty * unit_price) AS order_total
FROM {{ ref('stg_order_items') }}
GROUP BY order_id
При использовании {{ ref('int_order_amounts') }} в другой модели dbt подставит этот SELECT как WITH-блок:
-- Результат компиляции зависимой модели
WITH int_order_amounts AS (
SELECT order_id, SUM(qty * unit_price) AS order_total
FROM stg_order_items
GROUP BY order_id
)
SELECT ...
FROM int_order_amounts
Когда использовать:
- Промежуточные расчёты, которые не нужны аналитикам напрямую
- Хочется уменьшить количество объектов в базе
- Логика используется в нескольких моделях
Сравнение материализаций¶
| Материализация | Объект в БД | Данные хранятся | Когда подходит |
|---|---|---|---|
view |
VIEW | Нет | Staging, лёгкие трансформации |
table |
TABLE | Да, пересоздаётся | Витрины, тяжёлые JOIN |
incremental |
TABLE | Да, добавляется дельта | Большие факт-таблицы, события |
ephemeral |
— (CTE) | Нет | Промежуточные расчёты |
Конфигурация моделей¶
В самом SQL-файле¶
Блок {{ config() }} в начале файла:
В dbt_project.yml¶
Настройки для всех моделей в папке:
# dbt_project.yml
models:
my_project:
staging:
+materialized: view # все модели в staging/ — view
+schema: staging
marts:
+materialized: table # все модели в marts/ — table
+schema: analytics
Приоритет конфигурации
Конфигурация в SQL-файле (config()) имеет приоритет над dbt_project.yml. Это позволяет задать общие настройки для папки и переопределить их для конкретной модели.
В schema.yml¶
Можно настроить модели через YAML-описания:
# models/marts/schema.yml
models:
- name: fct_revenue
config:
materialized: table
tags: ['finance', 'daily']
description: "Таблица выручки по заказам"
Jinja в моделях¶
dbt использует шаблонизатор Jinja2. Это позволяет писать динамический SQL.
Переменные и условия¶
SELECT
order_id,
status,
created_at
FROM {{ ref('stg_orders') }}
{% if target.name == 'dev' %}
WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'
{% endif %}
target.name содержит имя среды из profiles.yml. В dev-среде модель обрабатывает только последние 30 дней — быстрее для разработки.
Переменные проекта (var)¶
-- Использование переменной
SELECT *
FROM {{ ref('stg_orders') }}
WHERE created_at >= '{{ var("start_date", "2024-01-01") }}'
Передача при запуске:
Макросы¶
Макросы — переиспользуемые SQL-шаблоны:
-- macros/cents_to_dollars.sql
{% macro cents_to_dollars(column_name) %}
ROUND({{ column_name }}::numeric / 100, 2)
{% endmacro %}
Использование в модели:
SELECT
order_id,
{{ cents_to_dollars('amount_cents') }} AS amount_dollars
FROM {{ ref('stg_payments') }}
Структура проекта: staging → intermediate → marts¶
staging¶
Первичная очистка сырых данных. Одна модель на один источник.
- Переименование колонок
- Приведение типов
- Фильтрация технических строк
- Материализация:
view
-- models/staging/stg_orders.sql
SELECT
id AS order_id,
user_id,
status,
total_cents,
created_at::timestamp AS created_at
FROM {{ source('raw', 'orders') }}
WHERE status != 'test'
intermediate¶
Промежуточные расчёты, которые используются в нескольких витринах.
- Агрегации на уровне записи
- JOIN нескольких staging-моделей
- Материализация:
ephemeralилиview
-- models/intermediate/int_order_payments.sql
{{ config(materialized='ephemeral') }}
SELECT
o.order_id,
o.user_id,
o.status,
p.payment_method,
p.amount
FROM {{ ref('stg_orders') }} o
LEFT JOIN {{ ref('stg_payments') }} p
ON o.order_id = p.order_id
marts¶
Финальные витрины для аналитиков и дашбордов.
- Бизнес-логика и метрики
- Материализация:
tableилиincremental - Именование:
fct_(факты),dim_(измерения)
-- models/marts/fct_revenue.sql
{{ config(materialized='table') }}
SELECT
o.order_id,
o.user_id,
d.full_name,
o.payment_method,
o.amount AS revenue,
o.order_date
FROM {{ ref('int_order_payments') }} o
LEFT JOIN {{ ref('dim_users') }} d
ON o.user_id = d.user_id
WHERE o.status = 'completed'
Полезные команды для работы с моделями¶
# Запустить одну модель
dbt run --select fct_revenue
# Запустить модель и все её зависимости (upstream)
dbt run --select +fct_revenue
# Запустить модель и всё, что от неё зависит (downstream)
dbt run --select fct_revenue+
# Запустить все модели в папке
dbt run --select staging.*
# Посмотреть скомпилированный SQL (без выполнения)
dbt compile --select fct_revenue
# Пересоздать incremental-модель с нуля
dbt run --select fct_events --full-refresh
Что запомнить¶
- Четыре материализации:
view,table,incremental,ephemeral view— по умолчанию, для staging-слояincremental— для больших таблиц с дельтой, используйis_incremental()иunique_key- Конфигурация:
config()в файле >dbt_project.yml>schema.yml - Jinja позволяет писать условия, использовать переменные и макросы
- Структура: staging (очистка) → intermediate (расчёты) → marts (витрины)
Проверь себя¶
Что дальше?¶
Модели готовы — теперь нужно убедиться, что данные корректны. Разберись с тестами и документацией — как dbt проверяет качество данных и генерирует документацию.
Источники¶
- dbt Documentation: Materializations — типы материализаций и их настройка
- dbt Documentation: Jinja and macros — шаблонизатор Jinja в dbt
- dbt Documentation: Model configurations — все параметры конфигурации моделей
- dbt Best Practices: How we structure our dbt projects — staging, intermediate, marts