dbt의 Jinja 및 매크로: 동적 SQL, 템플릿 및 사용자 정의 매크로
Jinja는 dbt를 SQL 도구에서 프로그래밍 가능한 변환 프레임워크로 변환합니다. DRY SQL을 위한 재사용 가능한 루프, 조건 및 매크로. dbt-utils(표준 라이브러리) 살펴보기 입력된 매개변수를 사용하여 사용자 정의 매크로를 작성하는 방법을 알아보세요.
DBT의 Jinja2: 정의 및 존재 이유
DBT 미국 진자2, 프로그래밍 가능성을 추가하기 위한 Python 템플릿 엔진
SQL에. 이중 중괄호 사이에 맞는 모든 것 {{ }}
Jinja 표현이며, 그 사이의 모든 것입니다. {% %} 그것은이다
제어문(if, for, set)
웨어하우스에서 각 모델을 실행하기 전, dbt 엮다 순수 SQL의 Jinja 템플릿.
폴더에서 컴파일된 SQL을 볼 수 있습니다. target/compiled/ 각각 후에 dbt run.
변수: var() 및 env_var()
dbt는 SQL 코드의 변수에 액세스하기 위한 두 가지 함수를 제공합니다.
var(): 프로젝트 변수
-- In dbt_project.yml puoi definire variabili globali:
# dbt_project.yml
vars:
start_date: '2024-01-01'
lookback_days: 30
payment_methods: ['credit_card', 'paypal', 'bank_transfer']
-- Usale nei modelli con var():
SELECT *
FROM {{ ref('stg_orders') }}
WHERE created_at >= '{{ var("start_date") }}'::date
-- Puoi sovrascrivere una variabile da CLI:
-- dbt run --vars '{"start_date": "2025-01-01", "lookback_days": 7}'
env_var(): 환경 변수
-- Accedi alle variabili d'ambiente del sistema
SELECT *
FROM {{ source('raw', 'events') }}
WHERE environment = '{{ env_var("DBT_ENVIRONMENT", "development") }}'
-- Il secondo parametro è il valore di default (opzionale)
-- Nei profiles.yml per le credenziali (prattica consigliata):
# profiles.yml
my_profile:
outputs:
prod:
type: snowflake
account: "{{ env_var('SNOWFLAKE_ACCOUNT') }}"
password: "{{ env_var('SNOWFLAKE_PASSWORD') }}"
템플릿의 if/else 조건
Jinja 조건문을 사용하면 다르게 동작하는 모델을 작성할 수 있습니다. 컨텍스트(환경, 변수, 구체화 유형)에 따라:
-- models/marts/finance/orders_with_taxes.sql
-- Logica di calcolo tasse diversa per paese
SELECT
order_id,
customer_id,
total_amount,
{% if var("target_market") == "US" %}
total_amount * 0.08 AS tax_amount, -- aliquota USA semplificata
{% elif var("target_market") == "IT" %}
total_amount * 0.22 AS tax_amount, -- IVA italiana
{% else %}
total_amount * 0.20 AS tax_amount, -- aliquota default EU
{% endif %}
total_amount + tax_amount AS total_with_tax
FROM {{ ref('stg_orders') }}
WHERE status = 'completed'
is_incremental(): 기본 패턴
내장 매크로 is_incremental() 증분 모델에서 추가하는 데 사용됩니다.
모델이 증분 모드로 실행될 때만 시간 필터
(완전 새로 고침이 아님):
-- models/marts/events_daily.sql
{{ config(materialized='incremental', unique_key='event_date') }}
SELECT
DATE_TRUNC('day', event_timestamp) AS event_date,
event_type,
COUNT(*) AS event_count,
COUNT(DISTINCT user_id) AS unique_users
FROM {{ ref('stg_events') }}
-- Questo blocco viene incluso SOLO nelle esecuzioni incrementali
{% if is_incremental() %}
WHERE event_timestamp > (SELECT MAX(event_date) FROM {{ this }})
{% endif %}
GROUP BY 1, 2
루프 대상: 동적 SQL 생성
Jinja 루프는 복사하여 붙여넣지 않고도 반복적인 SQL을 생성하는 데 매우 강력합니다.
-- Genera colonne per i giorni della settimana dinamicamente
SELECT
customer_id,
order_date,
{% for day_num in range(1, 8) %}
SUM(CASE WHEN DAYOFWEEK(order_date) = {{ day_num }}
THEN total_amount
ELSE 0 END) AS revenue_day_{{ day_num }}
{% if not loop.last %},{% endif %}
{% endfor %}
FROM {{ ref('stg_orders') }}
GROUP BY 1, 2
-- Pivot di metriche da una lista di variabile
{% set metrics = ['revenue', 'order_count', 'avg_order_value'] %}
SELECT
month,
region,
{% for metric in metrics %}
SUM(CASE WHEN metric_name = '{{ metric }}' THEN metric_value END) AS {{ metric }}
{%- if not loop.last %},{% endif %}
{% endfor %}
FROM {{ ref('metrics_unpivoted') }}
GROUP BY 1, 2
매크로: 재사용 가능한 SQL 함수
매크로는 dbt의 코드 재사용 메커니즘입니다: 매개변수를 취하는 Jinja 함수
그리고 SQL을 반환합니다. 그들은 디렉토리로 들어갑니다 macros/.
단순 매크로: Null 값 정리
-- macros/utils/safe_divide.sql
-- Divisione sicura che evita division by zero
{% macro safe_divide(numerator, denominator, default_value=0) %}
CASE
WHEN {{ denominator }} = 0 OR {{ denominator }} IS NULL
THEN {{ default_value }}
ELSE {{ numerator }} / {{ denominator }}
END
{% endmacro %}
-- Utilizzo nel modello:
SELECT
customer_id,
total_revenue,
order_count,
{{ safe_divide('total_revenue', 'order_count') }} AS avg_order_value
FROM {{ ref('customer_summary') }}
고급 매크로: 동적 UNION ALL 생성
-- macros/union_relations.sql
-- Crea UNION ALL da una lista di ref()
{% macro union_all_tables(relations) %}
{% for relation in relations %}
SELECT
'{{ relation }}' AS source_table,
*
FROM {{ ref(relation) }}
{% if not loop.last %}UNION ALL{% endif %}
{% endfor %}
{% endmacro %}
-- Utilizzo:
-- {{ union_all_tables(['events_jan', 'events_feb', 'events_mar']) }}
run_query()를 사용한 매크로: 매크로에서 창고 쿼리
-- macros/get_column_values.sql
-- Recupera valori distinti da una colonna per uso in loop
{% macro get_column_values(table, column) %}
{% set query %}
SELECT DISTINCT {{ column }}
FROM {{ ref(table) }}
ORDER BY 1
{% endset %}
{% set results = run_query(query) %}
{% if execute %} -- execute è False durante la fase di parsing
{% set values = results.columns[0].values() %}
{% do return(values) %}
{% else %}
{% do return([]) %}
{% endif %}
{% endmacro %}
-- Utilizzo per un pivot dinamico:
{% set regions = get_column_values('stg_orders', 'region') %}
SELECT
order_date,
{% for region in regions %}
SUM(CASE WHEN region = '{{ region }}' THEN revenue END) AS revenue_{{ region | lower | replace(' ', '_') }}
{%- if not loop.last %},{% endif %}
{% endfor %}
FROM {{ ref('orders_daily') }}
GROUP BY 1
dbt-utils: 표준 라이브러리
dbt-utils dbt 생태계에서 가장 많이 사용되는 패키지입니다. 이는 모든 프로젝트가 처음부터 다시 만들어질 수 있는 일반적인 매크로를 제공합니다.
# packages.yml
packages:
- package: dbt-labs/dbt_utils
version: 1.3.0
# Installa con:
# dbt deps
가장 많이 사용되는 dbt-utils 매크로
-- 1. generate_surrogate_key: chiave surrogata da più colonne (hash MD5)
SELECT
{{ dbt_utils.generate_surrogate_key(['order_id', 'customer_id']) }} AS sk,
order_id,
customer_id
FROM {{ ref('stg_orders') }}
-- 2. unpivot: trasforma colonne in righe (simile a UNPIVOT SQL)
{{ dbt_utils.unpivot(
relation=ref('orders_pivoted'),
cast_to='float',
exclude=['order_date', 'customer_id'],
field_name='metric_name',
value_name='metric_value'
) }}
-- 3. date_spine: genera una sequenza di date continua (per riempire i gap)
WITH date_spine AS (
{{ dbt_utils.date_spine(
datepart="day",
start_date="cast('2024-01-01' as date)",
end_date="current_date"
) }}
),
orders AS (
SELECT DATE_TRUNC('day', created_at) AS order_date, SUM(amount) AS revenue
FROM {{ ref('stg_orders') }}
GROUP BY 1
)
-- LEFT JOIN per avere 0 anche nei giorni senza ordini
SELECT
d.date_day,
COALESCE(o.revenue, 0) AS revenue
FROM date_spine d
LEFT JOIN orders o ON d.date_day = o.order_date
-- 4. pivot: trasforma righe in colonne
{{ dbt_utils.pivot(
column='status',
values=['completed', 'pending', 'cancelled'],
agg='count',
then_value='order_id'
) }}
매크로 모범 사례
품질 매크로에 대한 지침
-
미국
if execute쿼리를 실행하는 매크로의 경우: DAG가 제공됩니다. 여러 번 구문 분석되었으며 모든 단계에서 실제 실행이 필요한 것은 아닙니다. - 문서 매크로 템플릿과 같은 방식으로 — dbt가 생성됩니다. Jinja 독스트링이 포함된 매크로에 대한 문서도 있습니다.
- 통합 패키지 선호 (dbt-utils, dbt-expectations) 바퀴의 재창조 — 수천 개의 프로젝트에서 테스트되었습니다.
- 매크로를 단순하게 유지하세요: 매크로를 읽기 어려운 경우 분할하거나 템플릿에서 명시적인 SQL로 논리를 표현하는 것이 더 나을 것입니다.
안티 패턴: 과도하게 설계된 매크로
가장 흔한 실수는 모든 것에 매크로를 사용하는 것입니다. 매크로는 다음의 레이어를 추가합니다. 코드를 읽기 어렵게 만드는 간접적인 방법입니다. 실제로 재사용 가능한 로직에 사용하세요. (프로젝트에서 3개 이상 사용). 한 번 또는 두 번 사용되는 SQL의 경우 명시적 코드는 다음과 같습니다. 더 유지 관리가 가능합니다.
결론 및 다음 단계
Jinja와 매크로를 사용하면 dbt는 단순한 SQL 실행자가 아닌 프레임워크가 됩니다. 프로그래밍 가능한 변환. 변수는 모델을 환경에 적응할 수 있게 만듭니다. 루프는 반복을 제거하고 매크로는 재사용 가능한 논리를 캡슐화합니다.
다음 기사에서는 생산 성능에 대한 중요한 주제를 다룹니다. 구체화. 보기, 테이블, 증분 모델 및 스냅샷을 사용하는 경우 — 각 데이터 세트에 적합한 전략을 선택하는 방법을 알아보세요.







