Перейти к основному содержимому

Когда следует использовать UDF вместо макроса?

И пользовательские функции (UDF), и макросы позволяют переиспользовать логику в dbt‑проекте, однако работают они принципиально по‑разному. Ниже — рекомендации, когда стоит использовать каждый из подходов.

Используйте UDF, когда:

 Вам нужна логика, доступная за пределами dbt

UDF создаются непосредственно в хранилище данных и могут использоваться BI‑инструментами, ноутбуками для data science, SQL‑клиентами или любыми другими инструментами, которые подключаются к вашему хранилищу. Макросы же работают только внутри dbt.

 Вы хотите стандартизировать нативные функции хранилища

UDF позволяют создавать переиспользуемые функции хранилища для валидации данных, пользовательского форматирования или бизнес‑специфичных вычислений, которые должны быть единообразными во всех инструментах работы с данными. После создания такие функции становятся частью каталога функций вашего хранилища.

 Вы хотите, чтобы dbt управлял жизненным циклом функции

dbt управляет UDF как частью выполнения DAG, гарантируя, что они будут созданы до моделей, которые на них ссылаются. Вы можете хранить определения UDF в системе контроля версий вместе с моделями, тестировать изменения в средах разработки и разворачивать их через CI/CD‑пайплайны.

 Jinja компилируется в момент создания, а не при каждом вызове функции

В конфигурации UDF можно использовать Jinja (циклы, условия, макросы, ref, source, var). dbt разрешает этот Jinja в момент создания UDF, и именно получившееся SQL‑тело сохраняется в хранилище.

Jinja влияет на функцию в момент её создания, тогда как аргументы влияют на неё во время выполнения в хранилище:

  • Разрешено: Jinja, зависящий от состояния проекта или времени сборки — например, var(“can_do_things”), статический ref(‘orders’) или логика, зависящая от окружения. Всё это вычисляется один раз при создании.
  • Запрещено: Jinja, зависящий от аргументов функции, передаваемых во время выполнения. Компилятор их не видит, поэтому динамический ref(ref_name) или условный Jinja на основе значений аргументов работать не будут.
 Вам нужна Python‑логика, выполняемая в вашем хранилище

Python UDF создаёт Python‑функцию непосредственно в хранилище данных, которую затем можно вызывать с помощью SQL.
Это упрощает применение сложных трансформаций, вычислений или логики, которые трудно или громоздко выразить на SQL.

Python UDF поддерживают условия и циклы внутри самой функции (с использованием синтаксиса Python) и выполняются во время выполнения запроса, а не на этапе компиляции, как макросы. В настоящее время Python UDF поддерживаются в Snowflake и BigQuery.

Используйте макросы, когда:

 Вам нужно генерировать SQL на этапе компиляции

Макросы динамически генерируют SQL до отправки его в хранилище (на этапе компиляции). Это критично для:

  • построения разного SQL для разных хранилищ;
  • генерации повторяющихся SQL‑паттернов (например, создания десятков похожих колонок);
  • создания целых определений моделей или DDL‑выражений;
  • динамических ссылок на модели в зависимости от структуры проекта.

UDF выполняются во время выполнения запроса в хранилище. Хотя в их определениях можно использовать Jinja, они не генерируют новые SQL‑запросы — это заранее определённые функции, которые просто вызываются из SQL.

Расширение UDF

В настоящее время поддерживаются SQL‑ и Python‑UDF. Поддержка Java и Scala UDF планируется в будущих релизах.

 Вам нужно генерировать DDL или DML‑выражения

В настоящее время поддерживаются SQL‑ и Python‑UDF. Поддержка Java и Scala UDF планируется в будущих релизах.

 Вам нужно адаптировать SQL под разные хранилища

Макросы могут использовать условную логику Jinja для генерации SQL, специфичного для конкретного хранилища (см. cross-database macros), что делает dbt‑проект переносимым между платформами.

UDF — это объекты, специфичные для хранилища. Даже несмотря на то, что в определениях UDF можно использовать Jinja, у каждого хранилища свой синтаксис создания функций, разные поддерживаемые типы данных и диалекты SQL. Для каждого поддерживаемого хранилища вам придётся определять отдельные файлы UDF.

 Вашей логике нужен доступ к контексту dbt

И макросы, и UDF могут использовать Jinja, а значит имеют доступ к переменным контекста dbt, таким как {{ ref() }}, {{ source() }}, переменные окружения и конфигурации проекта. Более того, можно вызывать макрос из UDF (и наоборот), комбинируя динамическую генерацию SQL с выполнением логики во время выполнения.

Однако ключевое различие между ними — это момент, когда выполняется логика:

  • Макросы выполняются на этапе компиляции, генерируя SQL до отправки его в хранилище.
  • UDF выполняются внутри хранилища во время выполнения запроса.
 Вы хотите избежать создания объектов в хранилище

Макросы ничего не создают в вашем хранилище; они лишь генерируют SQL на этапе компиляции. UDF же создают реальные объекты функций в хранилище, которыми нужно управлять.

Можно ли использовать оба подхода вместе?

Да! Вы можете использовать макрос для вызова UDF или вызывать макрос внутри UDF, сочетая преимущества обоих подходов. Например, следующий пример показывает, как использовать макрос для задания значений аргументов по умолчанию вместе с логикой UDF:

{% macro cents_to_dollars(column_name, scale=2) %}
{{ function('cents_to_dollars') }}({{ column_name }}, {{scale}})
{% endmacro %}

Нашли ошибку?

0
Loading