statement 블록
statement 블록
(앱: dbt v2.0 이상)
statement는 데이터베이스에 쿼리를 날리고 결과를 Jinja 컨텍스트에 반환하는 SQL 쿼리예요. 다음은 users 테이블에서 모든 state를 가져오는 statement 예시예요.
출처: 문서
본문
dbt v2에서 statement 블록을 쓸 때 — 결과를 가져와야 하는 쿼리(예: 매크로나 Jinja 코드가 데이터베이스에서 반환된 데이터를 써야 할 때)에는
fetch_result=True를 가진statement블록이나run_query매크로를 쓸 수 있어요. DDL이나 유틸리티 작업(예:OPTIMIZE,VACUUM, 유지보수 쿼리)에서는 Jinja에서 결과에 접근할 필요가 없을 때fetch_result=False를 가진statement블록을 사용해요. 이렇게 하면 non-nullable로 선언된 컬럼에NULL값이 포함된 결과 셋을 처리할 때 실패할 수 있는 dbt v2의 엄격한 타입 검사 문제를 피할 수 있어요.
get_states_statement.sql
-- depends_on: {{ ref('users') }}
{%- call statement('states', fetch_result=True) -%}
select distinct state from {{ ref('users') }}
{%- endcall -%}
statement 블록의 시그니처는 이렇게 생겼어요:
statement(name=None, fetch_result=False, auto_begin=True)
statement를 실행할 때 dbt는 다른 dbt 모델이나 리소스에 대한 참조를 어떻게 해석할지 이해해야 해요. statement 블록 밖에서 이미 모델을 ref했다면 의존성이 자동으로 추론되지만, 그렇지 않다면 -- depends_on으로 의존성을 강제해야 해요.
-- depends_on 사용 예시
-- depends_on: {{ ref('users') }}
{% call statement('states', fetch_result=True) -%}
select distinct state from {{ ref('users') }}
/*
The unique states are: {{ load_result('states')['data'] }}
*/
{%- endcall %}
ref() 함수 사용 예시
{% call statement('states', fetch_result=True) -%}
select distinct state from {{ ref('users') }}
/*
The unique states are: {{ load_result('states')['data'] }}
*/
{%- endcall %}
select id * 2 from {{ ref('users') }}
인자(Args):
name(string): 이 statement가 반환한 결과 셋의 이름fetch_result(bool): True면 statement의 결과를 Jinja 컨텍스트에 로드auto_begin(bool): True면 트랜잭션이 없을 때 트랜잭션을 염. False면 트랜잭션을 열지 않음
statement 블록이 실행된 후에는 load_result 함수로 결과 셋에 접근할 수 있어요. 결과 객체는 세 개의 키를 포함해요:
response: 데이터베이스에서 반환된 메타데이터를 담은 구조화된 객체로, 어댑터에 따라 달라져요. 예: 성공code,rows_affected수, 총bytes_processed등. Result 객체의adapter_response와 비슷해요.data: 쿼리가 반환한 데이터의 Python 표현(배열, 튜플, 사전)table: 쿼리가 반환한 데이터의 Agate 테이블 표현
위 statement의 경우 다음과 같을 수 있어요:
load_states.sql
{%- set states = load_result('states') -%}
{%- set states_data = states['data'] -%}
{%- set states_status = states['response'] -%}
반환된 data 필드의 내용은 행렬(matrix)이에요. 각 행이 데이터베이스가 반환한 값들의 리스트인 행 리스트를 담고 있어요. 위 예시에서 이 데이터 구조는 다음과 같을 수 있어요:
states.sql
>>> log(states_data)
[
['PA'],
['NY'],
['CA'],
...
]
Fire and forget 작업
(앱: dbt v2.0 이상)
결과가 필요 없는 DDL이나 유틸리티 작업(반환된 행을 Jinja에서 쓰지 않기 때문에)에서는 fetch_result=False로 설정해요. 이건 Databricks에서 OPTIMIZE나 VACUUM 같은 작업에 권장되는 패턴이에요. 이런 작업은 non-nullable 컬럼에 null 값이 포함될 수 있는 결과 셋을 반환하거든요.
macros/optimize_table.sql
{% macro optimize_table(table, zorder_fields=[]) %}
{% set zorder_str = zorder_fields | join(', ') %}
{% set query %}
OPTIMIZE {{ table }}
{% if zorder_str | length > 0 %}
ZORDER BY ({{ zorder_str }})
{% endif %}
{% endset %}
{% call statement('optimize', fetch_result=False) %}
{{ query }}
{% endcall %}
{% endmacro %}
이 매크로를 post-hook에서 쓸 수 있어요:
dbt_project.yml
models:
my_project:
+post-hook:
- "{{ optimize_table(this, ['customer_id', 'order_date']) }}"
더 알아보기 (Learn more)
- 결과가 필요하면
fetch_result=True, 필요 없으면fetch_result=False로 statement 블록을 써요. - 관련 개념: run_query, load_result.