ref() и автоматический DAG
Главная магия dbt: вы не задаёте порядок запуска — dbt выводит его сам из ссылок ref().
ref() — Jinja-функция, которая ссылается на другую модель по имени; из всех
ref()dbt строит DAG (направленный ациклический граф) и вычисляет правильный порядок запуска.
Проблема: кто за кем
Витрины строятся слоями: модель orders читает customers, а revenue_report читает orders. Если запустить их в неверном порядке, поздняя модель прочитает несуществующую или вчерашнюю таблицу. В голом SQL порядок держат руками. dbt снимает эту заботу.
Как выглядит ссылка
Вместо хардкода имени таблицы вы пишете {{ ref('имя_модели') }}:
-- models/orders_enriched.sql
select
o.order_id,
o.amount,
c.email
from {{ ref('orders') }} as o
join {{ ref('customers') }} as c
on o.customer_id = c.customer_id
Здесь модель orders_enriched зависит от orders и customers. dbt видит эти ref() и понимает: сначала надо построить orders и customers, и только потом orders_enriched.
DAG: граф порядка
customers ---+
+--> orders_enriched --> revenue_report
orders ------+
Порядок, который выведет dbt:
1. customers, orders (можно параллельно)
2. orders_enriched
3. revenue_report
Граф направленный (стрелки задают зависимость) и ациклический (циклов нет — модель не может зависеть сама от себя через цепочку). По нему dbt делает топологическую сортировку и запускает модели волнами, параллеля независимые ветви.
Почему именно ref(), а не имя таблицы
Помимо порядка, ref() решает ещё две задачи. Во-первых, он подставляет правильную схему: в dev — вашу личную, в prod — общую. Один и тот же код работает в обоих окружениях. Во-вторых, он делает зависимости явными и проверяемыми — dbt сразу скажет, если вы сослались на несуществующую модель.
Как работает под капотом
При компиляции dbt разбирает Jinja в каждой модели и собирает все вызовы ref() в граф. {{ ref('orders') }} превращается в полностью квалифицированное имя analytics.dbt_dev.orders в зависимости от цели. Затем dbt строит DAG, проверяет отсутствие циклов и сортирует узлы топологически. dbt run идёт по этому порядку, а threads определяет, сколько независимых узлов считается одновременно.
Частые ошибки
- Хардкодить имя таблицы вместо
ref(). Тогда dbt не увидит зависимость, порядок сломается, и код не переедет между окружениями. - Создавать цикл. Если A через цепочку ссылается на себя, dbt откажется строить граф — DAG не допускает циклов.
- Ссылаться на источник через
ref(). Сырые внешние таблицы — этоsource()(следующий урок), а неref().
Итоги
ref()связывает модели и делает зависимости явными.- Из всех
ref()dbt строит DAG и сам выводит порядок запуска — это ключевая идея инструмента. ref()ещё и подставляет правильную схему, так что код одинаков в dev и prod.