ГлавнаяБлогМакросы в SQL: Jinja против нативного подхода
Алгоритмы

Макросы в SQL: Jinja против нативного подхода

Разбираем макросы в SQL: Jinja, нативные макросы и AST-подход. Узнайте, как избежать пяти вариантов кода и сделать пересборку моделей корректной. Читайте и применяйте!

Al
Редакция Algolitalgolit.ru
8 мин чтения16 августа 2026 г.

Почему макросы в SQL — это боль?

Если вы работали с dbt, то знаете: макросы в Jinja — это пять вариантов одного кода для разных диалектов. Но есть способ проще. В этой статье разберём, как макросы работают в Jinja, в DuckDB и в новом инструменте Interlace, который расширяет их на уровне AST. Вы узнаете, как сократить дублирование и сделать пересборку моделей предсказуемой.

Что такое макрос на самом деле

Макрос — это именованное выражение с параметром. Например, cents_to_dollars — это просто деление на 100 с приведением к decimal. В Jinja макрос — это текст, который подставляется до парсинга SQL. Поэтому для каждого диалекта нужен свой вариант: Postgres требует каст перед делением, BigQuery — тип NUMERIC и round.

В DuckDB есть нативный синтаксис:

-- macros/money.sql
CREATE MACRO cents_to_dollars(amount) AS (amount / 100)::numeric(16, 2);

Теперь можно вызывать макрос в любой модели:

-- models/stg_orders.sql
SELECT order_id,
       cents_to_dollars(subtotal) AS subtotal,
       cents_to_dollars(tax_paid) AS tax_paid
FROM raw_orders

Три места, где можно развернуть макрос

Макрос должен быть заменён своим телом в один из трёх моментов. От этого зависит всё.

1. В тексте до парсинга (Jinja)

Подстановка происходит до понимания SQL, поэтому для разных движков нужны разные тексты — отсюда пять вариантов. Вы теряете дерево запроса: после рендера структура исчезает.

2. В хранилище (CREATE MACRO)

DuckDB и Postgres поддерживают нативные макросы. Регистрируете макрос один раз, и все модели его вызывают. Но это ломает инвалидацию: если изменить макрос, модели не пересоберутся, потому что их SQL не изменился. Это тихая ошибка, которую обнаружат через три недели.

3. В AST на этапе компиляции (Interlace)

Interlace разворачивает макрос после парсинга, но до вычисления отпечатка и построения графа зависимостей. Это даёт все преимущества: один макрос для всех диалектов, корректная пересборка и видимость в lineage.

Одно определение — все диалекты

Поскольку разворачивание даёт AST, а не текст, транслятор (sqlglot) сам заботится о диалектах. Для макроса cents_to_dollars:

  • DuckDB: CAST((subtotal / 100) AS DECIMAL(16, 2))
  • Postgres: CAST((CAST(subtotal AS DOUBLE PRECISION) / NULLIF(100, 0)) AS DECIMAL(16, 2))
  • Snowflake: CAST((subtotal / NULLIF(100, 0)) AS DECIMAL(16, 2))
  • BigQuery: CAST((subtotal / NULLIF(100, 0)) AS NUMERIC)

Вам не нужно писать postgres__cents_to_dollars — sqlglot знает особенности диалектов. Это работает для обычных случаев; специфичные вещи всё равно потребуют внимания.

Изменение макроса пересобирает зависимые модели

Так как разворачивание включено в канонический SQL, отпечаток модели учитывает тело макроса. Изменили 100 на 1000 — и plan показывает пересборку всех моделей, которые вызывают макрос или зависят от них. Не нужно ничего декларировать — граф зависимостей работает сам.

Lineage видит тело макроса

Когда макрос разворачивается, lineage видит все источники данных, даже те, что не упомянуты в вызове. Например, для макроса net(x) AS x - shipping_fee lineage покажет net_total ← raw_orders.subtotal, raw_orders.shipping_fee. Если оставить макрос как чёрный ящик, shipping_fee исчезнет из графа — и это сломает анализ влияния.

Аналогично, если макрос ссылается на модель (например, in_gbp использует fx_rates), все вызывающие его модели получат зависимость от fx_rates автоматически.

Правила и ограничения

  • Только скалярные выражения. Табличные макросы не поддерживаются — это уже модели.
  • Макросы могут вызывать другие макросы (глубина до 10). Рекурсия — ошибка компиляции.
  • Количество аргументов проверяется на этапе компиляции.
  • Определения — в macros/*.sql, путь настраивается через macro_paths.

Что вы теряете

Макрос не существует в хранилище. Нельзя вызвать cents_to_dollars в ad-hoc запросе. Если вам это критично, используйте нативный CREATE MACRO — но тогда вы сами отвечаете за инвалидацию.

Практический вывод

Попробуйте Interlace: pip install interlaced. Посмотрите пример examples/jaffle-shop — там макросы используются для случаев из демо-проекта dbt. Вы увидите, как один макрос заменяет пять и как пересборка становится корректной. Начните с простого макроса в своём проекте и оцените разницу.

#макросы#SQL#Jinja#dbt#AST
Al
Редакция Algolit

Пишем про алгоритмы, подготовку к собеседованиям и карьеру в IT — так, чтобы было понятно и полезно.

Хочешь закрепить знания на практике?

Решай задачи на Algolit — интерактивная платформа для обучения

Начать бесплатно →