Раздел 26 · Data Engineering

Макросы и пакет dbt_utils

junior~15 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Опоздание в минутах «Гонец» считает уже в трёх местах: в int_deliveries, в отчёте по курьерам и в черновике витрины для бухгалтерии. Везде одна и та же длинная формула round(extract(epoch from (delivered_at - promised_at)) / 60). Вчера Отто попросил считать опоздание от времени забора, а не от обещанного. Ты поправил два места из трёх, и цифры на дашборде разъехались. Пора вынести формулу туда, где её правят один раз.

Рецепт на холодильнике

Бабушкин рецепт пирога висит на холодильнике. Когда пекут пирог, его не переписывают заново, а говорят «по бабушкиному рецепту, только вдвое больше». Поменяла бабушка сахар на мёд, и все следующие пироги сразу с мёдом.

В прошлом уроке Jinja уже генерировала SQL циклом внутри одной модели. Рецепт на холодильнике это следующий шаг: кусок SQL с именем, который зовут из любой модели.

Макрос это функция, которая возвращает SQL

Макрос в dbt это функция на Jinja. Она принимает аргументы и возвращает кусок текста, который подставляется в модель при компиляции. Макросы живут в папке macros/:

-- macros/minutes_between.sql
{% macro minutes_between(start_col, end_col) -%}
    round(extract(epoch from ({{ end_col }} - {{ start_col }})) / 60)
{%- endmacro %}

Внутри {% macro %} обычный шаблон: аргументы подставляются через {{ }}. Минусы у тегов -%} и {%- съедают пробелы и переводы строк вокруг, чтобы в модель не попали пустые строки. Вызов выглядит как вызов функции:

-- models/intermediate/int_deliveries.sql
select
    order_id,
    {{ minutes_between('promised_at', 'delivered_at') }} as delay_min
from {{ ref('stg_orders') }}

Обрати внимание на кавычки: 'promised_at' это строка, имя колонки. Макрос не знает про данные, он склеивает текст. После компиляции на месте вызова стоит та самая длинная формула, и база получает обычный SQL.

Теперь просьба Отто правится в одном файле. Поменял макрос, и все модели, которые его зовут, при следующем запуске считают по-новому.

Чужие рецепты: пакеты

Многие макросы нужны всем: суррогатный ключ из нескольких колонок, «все колонки, кроме двух», проверка диапазона значений. Писать их в каждом проекте незачем. Их собирают в пакеты, как библиотеки в программировании. Самый известный, dbt_utils, поддерживает сама dbt Labs.

Пакеты перечисляют в файле packages.yml в корне проекта:

packages:
  - package: dbt-labs/dbt_utils
    version: 1.4.1

Ставит их команда dbt deps: скачивает код пакета в папку dbt_packages/. Пока пакет не поставлен, dbt отказывается собирать проект и прямо об этом говорит: в packages.yml один пакет, а в dbt_packages ноль, запусти dbt deps. Папку dbt_packages/ в git не кладут, её создаёт dbt deps на каждой машине.

Макросы пакета зовут через его имя: dbt_utils.<макрос>. Так два пакета с макросом одного имени друг другу не мешают.

урок из курса

Урок из курса «Data Engineering». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.