100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Macros

Learn how dbt macros turn repeated Jinja + SQL patterns into reusable, function-like building blocks, and how community packages like dbt_utils extend them further.

ModelsIntermediate9 min readJul 10, 2026
Analogies

What a Macro Is

A macro is a reusable chunk of Jinja + SQL defined with {% macro macro_name(args) %} ... {% endmacro %} and saved in a .sql file inside the macros/ directory, callable from any model using {{ macro_name(args) }} just like a function call. Macros exist to eliminate copy-pasted SQL patterns — instead of writing the same CASE WHEN currency-conversion logic in fifteen different models, you write it once as a macro and call it everywhere it's needed.

🏏

Cricket analogy: This is like a bowling coach codifying a signature slower-ball technique once and teaching it to every bowler on the team, rather than each bowler independently reinventing their own version of the same delivery from scratch.

sql
-- macros/cents_to_dollars.sql
{% macro cents_to_dollars(column_name, decimal_places=2) %}
    round({{ column_name }} / 100.0, {{ decimal_places }})
{% endmacro %}

-- usage in a model
select
    order_id,
    {{ cents_to_dollars('amount_cents') }} as amount_usd
from {{ ref('stg_orders') }}

Built-in Macros and dbt_utils

dbt ships several built-in macros you use constantly without writing them yourself, like {{ dbt_utils.generate_surrogate_key([...]) }} from the popular dbt_utils package for hashing multiple columns into a single primary key, or {{ dbt_utils.date_spine(...) }} for generating a calendar table. Installing a community package like dbt_utils via packages.yml and running dbt deps gives your whole team access to dozens of battle-tested macros for common patterns like pivoting, unioning tables with different schemas, and testing, without anyone having to reinvent them.

🏏

Cricket analogy: This is like a cricket board adopting the internationally standardized DRS (Decision Review System) technology rather than every individual league inventing its own incompatible review process from scratch.

yaml
# packages.yml
packages:
  - package: dbt-labs/dbt_utils
    version: [">=1.1.0", "<2.0.0")]

Run dbt deps after adding a package to packages.yml to install it into the dbt_packages/ directory — this must be re-run any time you change a package version or add a new dependency.

Macro Scope and Overriding Materializations

Macros can accept default argument values, call other macros, and even use {% set %} to define local variables, giving them real function-like behavior beyond simple text substitution — but unlike a model, a macro produces no table or view on its own; it only exists to be called from somewhere else. Advanced teams can even override built-in dbt materializations themselves, such as redefining the incremental materialization's default merge behavior, by creating a macro with the exact reserved name dbt expects, like default__incremental, inside their own project or a package.

🏏

Cricket analogy: This is like a franchise's specialist fielding-drill routine that only exists to be run during training sessions — it produces no match result on its own, but every actual match performance depends on players having internalized it.

Overriding a built-in materialization macro like default__incremental is powerful but risky — it changes behavior for every incremental model in the project (and any package that depends on it), so test thoroughly in a non-production environment before adopting it.

  • A macro is reusable Jinja + SQL defined with {% macro %}...{% endmacro %} and called like a function.
  • Macros eliminate copy-pasted SQL by centralizing common patterns in one place.
  • dbt_utils and other community packages provide battle-tested macros installed via packages.yml and dbt deps.
  • Macros support default argument values, local {% set %} variables, and calling other macros.
  • A macro produces no table/view on its own — it only exists to be invoked from a model or another macro.
  • Advanced teams can override built-in materialization macros, such as default__incremental, to change dbt's core behavior.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#DbtDataBuildToolStudyNotes#Macros#Macro#Built#Dbt#Utils#StudyNotes#SkillVeris#ExamPrep