dbt для аналитика: шпаргалка
Что такое dbt
dbt (Data Build Tool) — инструмент для transformation данных в DWH. SQL + версионирование + тесты + документация в одном.
Концепция: ELT вместо ETL. Сначала загружаем сырые данные в DWH, потом трансформируем через SQL в dbt.
Структура проекта
dbt_project/
├── dbt_project.yml # конфигурация
├── models/
│ ├── staging/ # очистка сырых данных
│ │ └── stg_orders.sql
│ ├── marts/ # бизнес-модели
│ │ ├── fct_orders.sql
│ │ └── dim_users.sql
│ └── schema.yml # тесты и документация
├── macros/ # Jinja-макросы
├── seeds/ # CSV для загрузки
├── tests/ # кастомные тесты
└── snapshots/ # Slowly Changing DimensionsПростая модель
-- models/staging/stg_orders.sql
{{ config(
materialized='view',
schema='staging'
) }}
SELECT
id AS order_id,
user_id,
amount,
status,
created_at
FROM {{ source('raw', 'orders') }}
WHERE status IS NOT NULL{{ source('raw', 'orders') }}— ссылка на сырую таблицу.{{ config(...) }}— настройки материализации.
Типы материализации
-- View: виртуальная, пересчитывается при каждом запросе
{{ config(materialized='view') }}
-- Table: физическая таблица, пересоздаётся при dbt run
{{ config(materialized='TABLE') }}
-- Incremental: добавляет только новые данные
{{ config(materialized='incremental') }}
-- Ephemeral: CTE, не материализуется
{{ config(materialized='ephemeral') }}Incremental пример
{{ config(materialized='incremental', unique_key='order_id') }}
SELECT * FROM {{ source('raw', 'orders') }}
{% if is_incremental() %}
WHERE created_at > (SELECT MAX(created_at) FROM {{ this }})
{% endif %}ref() — ссылки между моделями
-- models/marts/fct_daily_revenue.sql
SELECT
DATE_TRUNC('day', created_at)::DATE AS day,
SUM(amount) AS revenue
FROM {{ ref('stg_orders') }}
WHERE status = 'paid'
GROUP BY 1{{ ref('stg_orders') }} — ссылка на другую модель. dbt автоматически строит DAG зависимостей.
Больше таких примеров с разборами — в Telegram-тренажёре. Короткие сессии, прогресс по темам, объяснения после каждого ответа.
Тесты
Generic тесты в schema.yml
version: 2
models:
- name: stg_orders
columns:
- name: order_id
tests:
- unique
- not_null
- name: user_id
tests:
- not_null
- relationships:
to: ref('stg_users')
field: user_id
- name: status
tests:
- accepted_values:
values: ['paid', 'pending', 'refunded']Кастомные тесты
-- tests/assert_positive_amounts.sql
SELECT * FROM {{ ref('stg_orders') }}
WHERE amount < 0
-- Тест фейлится, если возвращаются строкиЗапуск
dbt test # все тесты
dbt test -s stg_orders # конкретная модельJinja и макросы
Переменные
{% SET currencies = ['USD', 'EUR', 'RUB'] %}
SELECT
{% for c IN currencies %}
SUM(CASE WHEN currency = '{{ c }}' THEN amount END) AS total_{{ c | lower }},
{% endfor %}
SUM(amount) AS total
FROM ordersМакросы
-- macros/calculate_revenue.sql
{% macro calculate_revenue(TABLE, date_col) %}
SELECT
DATE_TRUNC('month', {{ date_col }})::DATE AS month,
SUM(amount) AS revenue
FROM {{ TABLE }}
GROUP BY 1
{% endmacro %}Использование:
{{ calculate_revenue(ref('stg_orders'), 'created_at') }}Документация
В schema.yml:
models:
- name: fct_orders
description: "Основная фактовая таблица заказов"
columns:
- name: order_id
description: "Уникальный ID заказа"
- name: amount
description: "Сумма заказа в рублях, до вычета комиссии"Генерируется HTML-доки:
dbt docs generate
dbt docs serve # открывает браузерКоманды
# Запустить все модели
dbt run
# Конкретную модель + её зависимости
dbt run -s +fct_orders+
# Только staging
dbt run -s staging
# Тесты
dbt test
# Документация
dbt docs generate && dbt docs serve
# Parse только (проверка синтаксиса)
dbt parse
# Full refresh incremental
dbt run -s fct_orders --full-refreshSnapshots — SCD Type 2
История изменений таблицы:
-- snapshots/users_snapshot.sql
{% snapshot users_snapshot %}
{{
config(
target_schema='snapshots',
unique_key='user_id',
strategy='check',
check_cols=['email', 'status']
)
}}
SELECT * FROM {{ source('raw', 'users') }}
{% endsnapshot %}Каждый запуск — проверяет изменения и сохраняет историю.
Если готовишься к собесу — бот @kariernik_bot закрывает 80% технических вопросов. SQL, Python, A/B, продуктовые метрики — всё в одном месте.
Seeds
CSV-файлы, которые загружаются как таблицы:
seeds/
country_list.csvdbt seedИспользуется для справочников: список стран, валют, категорий.
Почему dbt
Плюсы
- Версионирование. Код в Git.
- Тесты. Автоматическая проверка качества.
- Документация. Автогенерация.
- DAG зависимостей. Не пересчитываешь всё.
- Модульность. Переиспользуемые макросы.
Минусы
- Learning curve. Jinja, концепции.
- Только SQL (и Python в dbt-py).
- Требует DWH. Не для transaction DB.
Аналитик и dbt
Junior
Писать staging-модели, базовые факты/dims.
Middle
Incremental модели, macros, тестирование.
Senior / Analytics Engineer
Архитектура проекта, performance, CI/CD.
В современных компаниях dbt — стандарт для transformation. Упоминание в резюме — плюс.
Читайте также
FAQ
dbt vs SQL-views?
dbt даёт тесты, версионирование, документацию и автоматическую пересборку. Plain views — только SQL.
Нужен ли dbt для маленькой команды?
Если меньше 5 моделей — можно без него. Если 10+ — dbt спасает от хаоса.
dbt Cloud или dbt Core?
Core — бесплатный, самоhosted. Cloud — платный с UI, IDE, CI/CD.
Python в dbt?
Есть dbt-py (для Snowflake, Databricks, BigQuery). Позволяет писать ML-feature engineering прямо в dbt.