dbt_utils Package: The Macros Every dbt Project Ends Up Needing
If youโve written more than a handful of dbt models, youโve probably already hit one of the problems dbt_utils solves โ generating a surrogate key from multiple columns, building a complete date range with no gaps, or unioning several similarly-shaped tables together. dbt_utils is the most widely installed community package in the dbt ecosystem precisely because these problems are so common that reinventing them per-project is wasted effort.
Installing dbt_utils
packages: - package: dbt-labs/dbt_utils version: [">=1.1.0", "<2.0.0"]dbt depsOnce installed, every macro is called with the dbt_utils. prefix, as covered generally in dbt Packages.
generate_surrogate_key: Composite Keys Done Correctly
Many source tables donโt have a clean single-column primary key โ an events table might only be uniquely identified by the combination of user_id, event_type, and timestamp. Hand-writing a hash of concatenated columns is error-prone (null handling, type casting, delimiter collisions). generate_surrogate_key handles all of it.
select {{ dbt_utils.generate_surrogate_key(['user_id', 'event_type', 'created_at']) }} as event_key, user_id, event_type, created_atfrom {{ ref('stg_events') }}This produces a consistent hash, correctly handling nulls (which would otherwise silently break naive string concatenation), across any warehouse dbt supports โ the exact SQL generated differs by warehouse dialect, but the macro abstracts that difference away entirely.
date_spine: Filling In Missing Dates
A revenue-by-day report with gaps on days with zero orders is a subtly wrong report โ a chart with missing data points instead of visible zeros. date_spine generates a complete, gapless sequence of dates you can left-join your actual data onto.
with date_range as ( {{ dbt_utils.date_spine( datepart="day", start_date="cast('2024-01-01' as date)", end_date="cast(current_date as date)" ) }})
select date_range.date_day, coalesce(sum(orders.amount), 0) as daily_revenuefrom date_rangeleft join {{ ref('stg_orders') }} as orders on date_range.date_day = orders.order_dategroup by 1order by 1Every day in the range appears in the output, whether or not any orders happened that day โ the difference between a report that shows โ$0 on March 3rdโ and one that simply omits March 3rd entirely and lets a reader draw the wrong conclusion.
union_relations: Combining Similarly-Shaped Tables
Manually writing union all across a dozen regional or sharded tables, and keeping column lists in sync as schemas evolve, gets tedious fast. union_relations handles column alignment automatically.
{{ dbt_utils.union_relations( relations=[ ref('stg_orders_us'), ref('stg_orders_eu'), ref('stg_orders_apac') ]) }}If one relation is missing a column the others have, union_relations fills it with null rather than failing the whole union โ a meaningfully safer default than a hand-written union all that breaks the moment schemas drift slightly out of sync.
Generic Test Macros: not_null_proportion, equal_rowcount, and More
dbt_utils isnโt only about data-generation macros โ it also ships generic tests beyond dbtโs built-in set, usable exactly like unique or not_null in a YAML file.
models: - name: stg_orders columns: - name: discount_pct tests: - dbt_utils.not_null_proportion: at_least: 0.95This test passes as long as at least 95% of rows have a non-null discount_pct, which is far more realistic for many real-world columns than a strict not_null test that fails the entire build over a small, expected percentage of missing values.
Other Frequently-Used Macros Worth Knowing
| Macro | What it does |
|---|---|
dbt_utils.star() | Selects all columns except a specified exclusion list |
dbt_utils.pivot() | Pivots row values into columns without hand-writing every case statement |
dbt_utils.get_column_values() | Returns distinct values of a column, usable to drive dynamic Jinja loops |
dbt_utils.date_trunc() | Cross-warehouse-compatible date truncation |
-- star() with exclusions โ useful for staging models cleaning raw tablesselect {{ dbt_utils.star(from=source('raw', 'orders'), except=["internal_notes", "_raw_metadata"]) }}from {{ source('raw', 'orders') }}dbt_expectations: A Complementary Package Worth Knowing
For teams whose data quality needs go beyond dbt_utilsโs testing macros, dbt_expectations (inspired by the Python library Great Expectations) adds a much larger library of statistical and distribution-based tests.
columns: - name: order_amount tests: - dbt_expectations.expect_column_values_to_be_between: min_value: 0 max_value: 100000 - dbt_expectations.expect_column_mean_to_be_between: min_value: 10 max_value: 500These go further than dbt_utilsโs simpler checks โ verifying not just that values fall in a range, but that a columnโs statistical properties (mean, standard deviation) stay within expected bounds run over run, which is useful for catching subtler data drift that a basic range check wouldnโt flag.
When Not to Reach for dbt_utils
Not every problem benefits from a generic macro. If your logic is genuinely specific to your business rules โ a custom churn definition, a company-specific revenue recognition rule โ writing your own macro or plain SQL is more transparent than forcing it through a generic tool that wasnโt designed for it. dbt_utils earns its place solving genuinely generic, cross-project problems; it isnโt a replacement for understanding your own data.
Common Mistakes
Not pinning the version. dbt_utils has had breaking changes across major versions โ an unpinned install can silently change macro behavior on a routine dbt deps run, exactly as described in dbt Packages.
Using union_relations without checking column type compatibility. It aligns column names, but a column thatโs an integer in one relation and a string in another will still cause a type error โ the macro solves missing columns, not type mismatches.
Forgetting dbt_utils. as case-sensitive namespace prefix. A macro call without the correct package prefix simply wonโt be found, producing a compilation error that looks like the macro doesnโt exist at all.
Summary
| Macro | Solves |
|---|---|
generate_surrogate_key | Composite/hashed primary keys with correct null handling |
date_spine | Gapless date ranges for accurate time-series reporting |
union_relations | Safely unioning tables with slightly different schemas |
not_null_proportion | Realistic null-tolerance testing beyond strict not_null |
dbt_utils is close to a standard library for dbt projects โ before writing a macro for a problem that feels generic, itโs worth five minutes checking whether this package already solved it, tested against a far larger set of edge cases than any single projectโs macro would be.