Jinja: шаблонизатор поверх SQL

SQL не умеет циклы и условия — Jinja добавляет их поверх, до того как запрос уйдёт в хранилище.

Jinja — шаблонизатор, который dbt применяет к моделям до отправки в хранилище: он раскрывает {{ ... }} и {% ... %} в обычный SQL, добавляя переменные, циклы и условия.

Две фазы: компиляция и выполнение

Ключ к пониманию Jinja — это два момента времени. Сначала dbt компилирует модель: раскрывает всю Jinja в чистый SQL. Только потом этот SQL выполняется в хранилище. Хранилище никогда не видит Jinja — оно получает обычный SQL. ref(), source(), config() из прошлых уроков — всё это и есть Jinja.

{{ ... }}  -- выражение: подставить значение (ref, переменную, результат макроса)
{% ... %}  -- инструкция: логика (if, for, set), сама в SQL не попадает

Переменные через set

{% set statuses = ['paid', 'shipped', 'delivered'] %}

select *
from {{ ref('stg_orders') }}
where status in (
  {% for s in statuses %}
    '{{ s }}'{% if not loop.last %},{% endif %}
  {% endfor %}
)

После компиляции это превратится в обычный SQL: where status in ('paid','shipped','delivered'). Цикл for сгенерировал список, а loop.last поставил запятые между элементами, но не после последнего.

Условия через if

Вы уже видели это в инкрементальных моделях — {% if is_incremental() %}. Условия позволяют включать куски SQL в зависимости от ситуации. Ещё пример — разное поведение в dev и prod:

select * from {{ ref('events') }}
{% if target.name == 'dev' %}
  where created_at >= current_date - 7   -- в dev берём только неделю, чтобы быстрее
{% endif %}

Как посмотреть результат компиляции

# Скомпилировать модели в чистый SQL (без выполнения)
dbt compile

# Результат лежит в target/compiled/...
# Полезно, чтобы увидеть, во что превратилась Jinja

Как работает под капотом

dbt — это, по сути, генератор SQL на Jinja. На этапе compile он проходит по файлу, исполняет все {% %}-инструкции и подставляет все {{ }}-выражения, получая финальный SQL-текст. Переменные, циклы и условия дают то, чего нет в чистом SQL: способность переиспользовать и параметризовать запросы. Но важно помнить: Jinja работает над текстом запроса, а не над строками данных. Цикл for разворачивает SQL-текст, а не перебирает записи таблицы.

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

  • Думать, что Jinja выполняется в хранилище. Нет — она раскрывается до отправки; хранилище видит чистый SQL.
  • Путать компиляцию и выполнение. Цикл for генерирует текст запроса, а не итерирует данные.
  • Перебарщивать с логикой. Слишком хитрая Jinja превращает модель в нечитаемую; сложное лучше вынести в макрос.

Итоги

  • Jinja добавляет к SQL переменные, циклы и условия; dbt раскрывает её в чистый SQL на этапе компиляции.
  • Хранилище видит только готовый SQL — Jinja работает над текстом запроса, не над данными.
  • dbt compile показывает, во что превратилась Jinja.
Проверьте себя
1. Когда раскрывается Jinja в модели dbt?
AВо время выполнения в хранилище
BНа этапе компиляции, до отправки SQL в хранилище
CНикогда, это просто комментарии
DПосле выполнения запроса
2. Над чем работает цикл {% for %} в Jinja?
AНад строками таблицы во время выполнения
BНад текстом SQL-запроса на этапе компиляции (генерирует SQL)
CНад файлами проекта
DНад подключениями к хранилищу