dbt на собеседовании Data Engineer

Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

Карьерник — Duolingo для аналитиков: 10 минут в день тренируй SQL, Python, A/B, статистику, метрики и ещё 3 темы собеса. 1500+ вопросов в Telegram-боте. Бесплатно.

Зачем спрашивают dbt

dbt стал стандартом моделирования в DWH в РФ за последние два года: Тинькофф, Авито, X5, Озон публично выкладывают шаблоны и доклады. Если в вакансии есть Snowflake/BigQuery/ClickHouse и слово «моделирование» — почти наверняка спросят про dbt.

Главная боль без dbt — SQL-скрипты, разбросанные по Airflow-DAG, без зависимостей, без тестов, без документации. dbt даёт три вещи: lineage (DAG моделей), материализацию (как сохранить результат) и тесты на данные. Кандидат, который путает «dbt — это инструмент трансформации» с «dbt — это оркестратор», на середине собеса плывёт.

Модели и ref/source

Модель.sql файл с одним SELECT-запросом. dbt оборачивает его в CREATE TABLE/VIEW согласно materialization.

-- models/marts/orders_daily.sql
SELECT
    DATE_TRUNC('day', created_at) AS order_date,
    COUNT(*) AS orders_cnt,
    SUM(amount) AS revenue
FROM {{ ref('stg_orders') }}
GROUP BY 1

ref() — ссылка на другую модель проекта. dbt по ref строит DAG зависимостей и решает порядок запуска.

source() — ссылка на сырую таблицу из внешнего источника. Декларируется в sources.yml:

sources:
  - name: raw
    tables:
      - name: orders

Использование: FROM {{ source('raw', 'orders') }}. Зачем source отдельно — ref нужен для зависимостей внутри проекта, source маркирует «вход в DWH из мира». На source-таблицы вешают freshness-проверки.

На собесе спросят: «В чём разница между ref и source?» Правильный ответ:

  • ref — модели, которые dbt сам создаёт
  • source — таблицы, которые загрузил кто-то другой (Airbyte, Fivetran, Airflow)

Materializations

Materialization — как dbt сохранит результат модели:

Тип Что создаёт Когда использовать
view VIEW Лёгкая трансформация, читают редко
table CREATE TABLE AS SELECT Тяжёлая модель, читают часто
incremental INSERT новых строк Большие таблицы фактов
ephemeral CTE в зависимых моделях Промежуточная логика, без материализации

Указывается в dbt_project.yml или прямо в модели:

{{ config(materialized='incremental', unique_key='order_id') }}

ephemeral — частая ловушка. Модель с ephemeral не материализуется в БД, а инлайнится как CTE в каждую модель, которая её ссылается через ref. Если на неё ссылаются 5 моделей и она тяжёлая — она пересчитается 5 раз. Использовать только для лёгких staging-преобразований.

Incremental-стратегии

Инкрементальная модель пересчитывает только новые/изменившиеся строки, не всю таблицу. Стратегии — как dbt будет мерджить новые данные:

  • append — просто INSERT новых строк. Дешёво. Не дедуплицирует — если придёт дубль, в таблице будет дубль.
  • merge — UPSERT по unique_key. Дороже, но обновляет существующие строки. Дефолт для Snowflake/BigQuery/Postgres 15+.
  • delete+insert — удалить строки по ключу, вставить заново. Используется для ClickHouse/Redshift, где нет нативного MERGE.
  • insert_overwrite — перезаписать партиции целиком. Лучший вариант для BigQuery/Spark с партиционированием.

Шаблон incremental-модели:

{{ config(
    materialized='incremental',
    unique_key='event_id',
    incremental_strategy='merge'
) }}

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 %}

Тонкость: фильтр в is_incremental() блоке должен брать данные с запасом. Если события приходят с задержкой 30 минут (late-arriving), то MAX(created_at) пропустит их. Безопаснее: created_at > MAX(created_at) - INTERVAL '1 hour' + unique_key для дедупликации через merge.

На собесе спросят: «Как сделать backfill incremental-модели?» Ответ: dbt run --full-refresh -s model_name — пересоздаст таблицу заново, игнорируя is_incremental().

Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

Тесты в dbt

Два уровня тестов:

Generic tests — встроенные, описываются в YAML:

models:
  - name: stg_orders
    columns:
      - name: order_id
        tests:
          - not_null
          - unique
      - name: status
        tests:
          - accepted_values:
              values: ['new', 'paid', 'cancelled']
      - name: user_id
        tests:
          - relationships:
              to: ref('stg_users')
              field: user_id

Четыре дефолтных теста — unique, not_null, accepted_values, relationships — закрывают 80% базовых проверок данных.

Singular tests.sql файлы в tests/. Запрос возвращает строки = тест упал:

-- tests/no_negative_revenue.sql
SELECT *
FROM {{ ref('orders_daily') }}
WHERE revenue < 0

Запуск: dbt test. В CI — dbt build запустит run + test + seed в правильном порядке с учётом DAG.

dbt-utils и dbt-expectations — пакеты с расширенными тестами (expression_is_true, equal_rowcount, статистические проверки).

Snapshots и SCD

Snapshot — реализация SCD Type 2 в dbt. Отслеживает изменения строк во времени, добавляя колонки dbt_valid_from и dbt_valid_to.

-- snapshots/orders_snapshot.sql
{% snapshot orders_snapshot %}

{{ config(
    target_schema='snapshots',
    unique_key='order_id',
    strategy='check',
    check_cols=['status', 'amount']
) }}

SELECT * FROM {{ source('raw', 'orders') }}

{% endsnapshot %}

Две стратегии:

  • timestamp — сравнивает по updated_at колонке. Если значение колонки новее, чем в snapshot — пишет новую версию. Дешёво.
  • check — сравнивает значения колонок из check_cols. Используется, когда нет надёжного updated_at.

Запуск: dbt snapshot. На каждый запуск — новые версии строк, у предыдущих закрывается dbt_valid_to.

На собесе спросят: «Когда snapshot, когда обычная инкрементальная модель?» Ответ: snapshot — когда нужна история изменений (адрес клиента менялся, нужен исторический срез на любую дату). Incremental — когда нужен только текущий снимок или append-only лог.

Частые ошибки

Использовать ephemeral для тяжёлых моделей. Каждая зависимая модель пересчитает её заново. Для тяжёлой логики — view или table.

Incremental без unique_key. При стратегии merge без ключа dbt не знает, как дедуплицировать. Будут дубли. С append — те же дубли, но без warning.

Полный refresh каждую ночь. Если incremental-модель падает — лень разбираться, ставят --full-refresh в cron. Через год эта табличка из 1 ТБ рестартится 4 часа. Лучше — алерт на падение и точечный backfill.

Тесты только в YAML. YAML-тесты простые, но не покрывают сложные инварианты. Singular tests в tests/ дают полноту.

source без freshness. Sources.yml без freshness — упустишь момент, когда источник перестал обновляться. Минимум — warn_after / error_after.

Снапшоты в production-схеме. Snapshots генерируют много версий, могут раздуть прод. Хранить в отдельной схеме snapshots.

Связанные темы

FAQ

dbt — это оркестратор, как Airflow?

Нет. dbt не запускает себя по расписанию и не дёргает внешние API. Это движок трансформации в DWH. В проде Airflow или dbt Cloud Scheduler запускают dbt run по расписанию.

dbt-core vs dbt Cloud — что выбирать?

dbt-core — open-source CLI, ставится pip, работает где угодно. dbt Cloud — SaaS поверх core: UI, scheduler, IDE, alerts. На собесе обычно ждут понимания core; Cloud — приятный плюс.

Как тестировать модели локально?

dbt run -s model_name запустит конкретную модель. dbt test -s model_name прогонит её тесты. dbt build -s model_name+ — модель и все downstream. dbt compile покажет финальный SQL без выполнения.

Что делать с медленными dbt-моделями?

Профилировать dbt_project_evaluator, смотреть граф через dbt docs serve, переводить тяжёлые VIEW в TABLE/incremental, добавлять кластеризацию/партиционирование на уровне DWH. В ClickHouse — материализованные представления вместо incremental.

dbt в ClickHouse работает нормально?

Через адаптер dbt-clickhouse. Есть особенности: merge стратегия эмулируется через delete+insert или ReplacingMergeTree, snapshots поддерживаются с ограничениями. Для тяжёлых джоинов лучше использовать материализованные представления Click House напрямую.

Это официальная информация?

Нет. Статья основана на публичной документации dbt и опыте команд. Конкретные требования к dbt-стеку зависят от компании.


Тренируйте Data Engineering — откройте тренажёр с 1500+ вопросами для собесов.