Как настроить dbt проект с нуля

Зачем dbt

dbt превращает SQL-запросы в версионируемые, тестируемые модели данных. Вместо ручного поддержания витрин в БД вы описываете их в SQL-файлах, и dbt строит таблицы в правильном порядке с проверками.

Для аналитика это мощный инструмент, который делает вашу работу воспроизводимой. Вместо «у меня есть дашборд, основанный на запросе, который я не помню, где лежит» — чёткая структура с документацией и тестами.

Установка

dbt бывает двух видов: dbt Core (open-source) и dbt Cloud (платная SaaS-версия). Для старта берите Core.

Установка через pip:

pip install dbt-postgres  # для PostgreSQL
# или
pip install dbt-bigquery
pip install dbt-snowflake
pip install dbt-clickhouse

Каждый adapter отдельно, в зависимости от вашей БД.

Проверка установки:

dbt --version

Создание проекта

dbt init my_project
cd my_project

dbt init создаёт скелет проекта. Внутри будут:

  • dbt_project.yml — главная конфигурация.
  • models/ — папка для SQL-моделей.
  • tests/ — кастомные тесты.
  • macros/ — переиспользуемый SQL.
  • seeds/ — CSV-файлы для загрузки.
  • snapshots/ — snapshot-модели.

Настройка соединения

dbt хранит credentials в ~/.dbt/profiles.yml. Файл выглядит так:

my_project:
  target: dev
  outputs:
    dev:
      type: postgres
      host: localhost
      user: my_user
      password: my_password
      port: 5432
      dbname: my_db
      schema: dev_analytics
      threads: 4

    prod:
      type: postgres
      host: prod-db.company.com
      user: prod_user
      password: "{{ env_var('DBT_PROD_PASSWORD') }}"
      port: 5432
      dbname: prod_db
      schema: analytics
      threads: 8

Несколько окружений (dev, prod) позволяют тестировать локально перед production deployment.

Пароль лучше через env var, чтобы не коммитить в git.

Первая модель

Модели — SQL-файлы в models/. Например, models/staging/stg_orders.sql:

{{ config(materialized='view') }}

SELECT
    id AS order_id,
    user_id,
    amount,
    status,
    created_at
FROM {{ source('raw', 'orders') }}
WHERE status IS NOT NULL

{{ config(materialized='view') }} — говорит dbt сделать это view (виртуальная таблица). Альтернатива — 'table' для физической таблицы.

{{ source('raw', 'orders') }} — ссылка на исходную таблицу. Она должна быть описана в sources.yml:

version: 2

sources:
  - name: raw
    schema: raw_data
    tables:
      - name: orders
        description: "Сырые заказы из production"

Попробовать силы на подобных вопросах проще всего в тренажёре Карьерник — прямо в Telegram, без регистрации через сайт.

Запуск модели

dbt run

Эта команда компилирует SQL-модели, строит DAG зависимостей и выполняет их в правильном порядке.

После запуска в вашей БД появится view dev_analytics.stg_orders (по target dev).

Чтобы запустить конкретную модель:

dbt run --select stg_orders

Чтобы запустить модель и всё, что от неё зависит:

dbt run --select stg_orders+

Цепочка моделей

Реальные проекты имеют несколько слоёв. Стандартная структура:

models/
├── staging/       # очистка сырых данных
│   ├── stg_orders.sql
│   └── stg_users.sql
├── intermediate/  # промежуточные расчёты
│   └── int_user_orders.sql
└── marts/         # финальные витрины
    ├── mart_user_metrics.sql
    └── mart_daily_revenue.sql

Каждая модель ссылается на предыдущие через ref():

-- models/marts/mart_daily_revenue.sql

{{ config(materialized='TABLE') }}

SELECT
    DATE_TRUNC('day', created_at)::DATE AS day,
    SUM(amount) AS revenue
FROM {{ ref('stg_orders') }}
WHERE status = 'paid'
GROUP BY 1
ORDER BY 1

{{ ref('stg_orders') }} — dbt заменит это на актуальное имя таблицы в нужной схеме.

Тесты

В models/schema.yml описываем ожидания от данных:

version: 2

models:
  - name: stg_orders
    description: "Очищенные заказы"
    columns:
      - name: order_id
        description: "Уникальный ID заказа"
        tests:
          - unique
          - not_null
      - name: user_id
        tests:
          - not_null
      - name: status
        tests:
          - accepted_values:
              values: ['paid', 'refunded', 'pending', 'cancelled']

Запускаем тесты:

dbt test

Если есть ошибки (неуникальный order_id, NULL где быть не должно, неожиданный status), dbt сообщает. Это защита от data quality issues.

Документация

dbt docs generate
dbt docs serve

generate собирает metadata из схемы и моделей. serve запускает локальный веб-сервер с интерактивной документацией. Видно dependencies, описания, тесты.

Для команды — это self-service каталог. Аналитики сами находят нужные таблицы без pingов DE.

Incremental models

Для больших таблиц полная перезапись медленная. Используйте 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 %}

При первом запуске dbt делает полную загрузку. Далее — только новые строки.

Пройти 30–50 задач по теме за вечер можно в Telegram-тренажёре. Это то, что отличает «знаю» от «уверенно отвечу на собесе».

Макросы

Переиспользуемый SQL — в макросах:

-- macros/calculate_retention.sql
{% macro calculate_retention_d7(TABLE) %}
    SELECT
        user_id,
        MIN(event_time) AS signup_time,
        BOOL_OR(event_time >= MIN(event_time) + INTERVAL '7 day'
                AND event_time < MIN(event_time) + INTERVAL '8 day') AS retained_d7
    FROM {{ TABLE }}
    GROUP BY user_id
{% endmacro %}

Использование:

{{ calculate_retention_d7(ref('stg_events')) }}

DRY-принцип: не дублировать одну логику в разных моделях.

CI/CD

Для команды — обязательно. При PR в git:

  • dbt parse — проверка синтаксиса.
  • dbt compile — компиляция без выполнения.
  • dbt run --models state:modified — только изменённые модели.
  • dbt test — все тесты.

Настраивается через GitHub Actions или Jenkins. Без CI/CD большие проекты быстро ломаются.

Типовые ошибки новичка

Использовать SELECT * в staging. Плохо — новые колонки ломают downstream. Лучше явно перечислять.

Сложные модели в staging-слое. Staging только для чистки, вся логика — в intermediate и marts.

Игнорировать тесты. «Потом добавлю» почти всегда означает «никогда». Пишите сразу.

Hardcode schema names. Используйте {{ ref() }} и {{ source() }}, dbt сам подставит правильные.

Один большой commit с десятками моделей. Маленькие PR, понятные reviews, быстрее feedback.

Читайте также

FAQ

dbt Core или dbt Cloud?

Core — бесплатный, гибкий. Cloud — платный с UI и CI/CD. Для команды 10+ — Cloud удобнее.

Сколько времени на освоение?

Базовые команды и модели — за день. Уверенное владение — 2-3 недели.

dbt заменяет Airflow?

Нет. dbt — transformation-инструмент. Airflow — orchestration. Часто используются вместе.

Где хранить CSV для seed?

В папке seeds/ проекта. Через dbt seed они загружаются в БД.