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

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

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

Зачем спрашивают на собесе DE

dbt — стандарт T в ELT. На собесе DE: «зачем sources», «как написать macro», «freshness».

Sources и ref

Source — внешняя таблица (raw layer), не управляемая dbt.

# sources.yml
sources:
  - name: raw_app
    schema: raw
    tables:
      - name: users
        columns:
          - name: id
            tests: [unique, not_null]
      - name: events

В моделях:

SELECT * FROM {{ source('raw_app', 'users') }}

Ref — ссылка на другую модель.

SELECT * FROM {{ ref('stg_users') }}

dbt автоматически строит DAG зависимостей через ref.

Source freshness

Проверка, что source данные не устарели.

sources:
  - name: raw_app
    tables:
      - name: events
        loaded_at_field: created_at
        freshness:
          warn_after: {count: 12, period: hour}
          error_after: {count: 24, period: hour}

dbt source freshness — проверяет.

Если created_at старше 24 часов — error в pipeline.

Macros и Jinja

dbt SQL — это Jinja-templated SQL.

SELECT *
FROM {{ ref('orders') }}
WHERE created_at >= '{{ var("start_date") }}'

var() — переменные.

Условная логика:

{% if target.name == 'prod' %}
  AND env = 'production'
{% ELSE %}
  AND env = 'staging'
{% endif %}

Loops:

{% for col IN ['name', 'email', 'phone'] %}
  COALESCE({{ col }}, '') AS {{ col }}_clean,
{% endfor %}
Готовься к собесу аналитика как в Duolingo
10 минут в день — SQL, Python, A/B, метрики. 1700+ вопросов в Telegram
Открыть Карьерник в Telegram

Custom macros

Reusable SQL snippets.

-- macros/cents_to_dollars.sql
{% macro cents_to_dollars(column_name) %}
  ({{ column_name }} / 100.0)::NUMERIC(10, 2)
{% endmacro %}

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

SELECT
  order_id,
  {{ cents_to_dollars('amount_cents') }} AS amount_dollars
FROM {{ ref('orders') }}

dbt_utils package. Стандартные макросы:

  • dbt_utils.surrogate_key([col1, col2]) — hash composite key.
  • dbt_utils.pivot(), unpivot().
  • dbt_utils.deduplicate().
  • dbt_utils.union_relations().

Hooks

Запуск SQL до / после dbt операций.

Pre-hook / post-hook (model-level):

{{ config(
    pre_hook="GRANT SELECT ON {{ this }} TO analyst_role",
    post_hook="VACUUM ANALYZE {{ this }}"
) }}

On-run-start / on-run-end (project-level в dbt_project.yml):

on-run-start: "CREATE SCHEMA IF NOT EXISTS {{ target.schema }}"
on-run-end: "GRANT USAGE ON SCHEMA {{ target.schema }} TO analyst_role"

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

Hard-coded имена таблиц. Без ref() / source() — dbt не знает зависимости. Lineage ломается.

Macros без документации. Через год команда не помнит, что делает custom macro.

Слишком сложные macros. Если macro имеет 50 строк — может быть отдельная модель.

Игнорировать freshness. Stale data загружается в downstream — никто не замечает.

Hard-coded environments в Jinja. Используй target.name или vars.

Не запускать dbt_utils тесты. Дублирование собственных тестов на каждую модель — лишний труд.

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

FAQ

Macros vs models?

Macro — reusable SQL fragment. Model — таблица / view. Если SQL генерирует data — model. Если transforms существующий — macro.

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

Нет. Статья основана на документации dbt 1.7+.


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