dbt中如何将动态生成的变量传入post_hook配置
问题背景
我需要在Snowflake中批量删除多张表,不想在dbt任务里写多条独立删表命令,希望只传入参数就能完成操作。我通过查询数据库INFORMATION_SCHEMA下的TABLES视图,找到了动态生成删表语句的思路。
最初编写的动态生成删表语句的SQL如下:
SELECT LISTAGG(CONCAT('"DROP TABLE IF EXISTS ', CONCAT(CONCAT_WS('.', TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME), '"')), ', ') FROM "{{ var('db_name') }}"."INFORMATION_SCHEMA"."TABLES" WHERE table_owner = 'TRANSFORM_ROLE' AND table_schema = '{{ var("schema_name") }}' AND table_name LIKE '%TEST%TABLE%'
我把这段语句放到dbt模型的call statement块中:
{%- call statement('generate_drop_table_statement', fetch_result=True) -%} SELECT LISTAGG(CONCAT('"DROP TABLE IF EXISTS ', CONCAT(CONCAT_WS('.', TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME), '"')), ', ') FROM "{{ var('db_name') }}"."INFORMATION_SCHEMA"."TABLES" WHERE table_owner = 'TRANSFORM_ROLE' AND table_schema = '{{ var("schema_name") }}' AND table_name LIKE '%TEST%TABLE%' {%- endcall -%}
接着加载查询结果到变量:
{%- set drop_statements = load_result('generate_drop_table_statement')['data'][0][0] -%}
之后我尝试把drop_statements变量传入config块的post_hook配置中:
{{ config( alias='DROP_STATEMENTS_TABLE', materialized='table', post_hook=['{{ drop_statements }}', "DROP TABLE IF EXISTS {{ var('db_name') }}.{{ var('schema_name') }}.FIRST_MODEL"] ) }}
运行时出现异常:post_hook里写死的硬编码删表语句可以正常执行,但动态生成存在drop_statements里的语句无法运行。我不确定是变量调用方式错误,还是dbt本身不支持这种用法。
完整模型代码
{%- call statement('generate_drop_table_statement', fetch_result=True) -%} SELECT LISTAGG(CONCAT('"DROP TABLE IF EXISTS ', CONCAT(CONCAT_WS('.', TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME), '"')), ', ') FROM "{{ var('db_name') }}"."INFORMATION_SCHEMA"."TABLES" WHERE table_owner = 'TRANSFORM_ROLE' AND table_schema = '{{ var("schema_name") }}' AND table_name LIKE '%TEST%TABLE%' {%- endcall -%} {%- set drop_statements = load_result('generate_drop_table_statement')['data'][0][0] -%} {{ log(drop_statements, info=True) }} {{ config( alias='DROP_STATEMENTS_TABLE', materialized='table', post_hook=['{{ drop_statements }}', "DROP TABLE IF EXISTS {{ var('db_name') }}.{{ var('schema_name') }}.FIRST_MODEL"] ) }} with statements as ( SELECT '{{ drop_statements }}' ) SELECT * FROM statements
问题原因
两个核心问题导致执行失败:
config块解析时不会对传入值做二次Jinja渲染:你在post_hook里写的'{{ drop_statements }}'会被直接识别为字符串字面量,不会替换为你之前赋值的变量内容,而硬编码的语句因为本身就是完整可执行的SQL字符串,所以可以正常运行- 你用
LISTAGG把多条DROP语句拼成逗号分隔的单个字符串,不符合Snowflake的SQL语法,单条SQL语句无法直接通过逗号拼接多条DDL执行
注:“pre_hook和post_hook仅接受字符串类型输入”的描述不准确,实际上hook支持传入字符串列表,列表内的每条字符串会作为独立SQL语句依次执行。
解决方案
调整实现逻辑,不要把多条语句拼成单个字符串传入hook,而是拆分为独立的语句列表,同时保证变量在config解析前已完成赋值:
- 修改生成删表语句的SQL,去掉
LISTAGG拼接逻辑,直接返回每行一条独立的DROP语句 - 把返回结果处理为字符串列表,直接传入
post_hook,不需要额外加Jinja插值标记包裹 - 把需要硬编码执行的语句也追加到同一个列表中,统一传入hook配置
修改后的完整代码如下:
{%- call statement('generate_drop_table_statement', fetch_result=True) -%} SELECT CONCAT('DROP TABLE IF EXISTS ', CONCAT_WS('.', TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME)) AS drop_stmt FROM "{{ var('db_name') }}"."INFORMATION_SCHEMA"."TABLES" WHERE table_owner = 'TRANSFORM_ROLE' AND table_schema = '{{ var("schema_name") }}' AND table_name LIKE '%TEST%TABLE%' {%- endcall -%} {%- set results = load_result('generate_drop_table_statement')['data'] -%} {# 把返回结果处理为独立语句的列表 #} {%- set drop_statements = [] -%} {%- for row in results -%} {%- do drop_statements.append(row[0]) -%} {%- endfor -%} {# 追加需要硬编码执行的删表语句 #} {%- do drop_statements.append("DROP TABLE IF EXISTS {{ var('db_name') }}.{{ var('schema_name') }}.FIRST_MODEL") -%} {{ config( alias='DROP_STATEMENTS_TABLE', materialized='table', post_hook=drop_statements ) }} SELECT 1 AS dummy
注意:必须确保
config块的调用位于所有变量赋值逻辑之后,不要在config里嵌套二次Jinja渲染的变量表达式,直接传入已经构造完成的字符串列表即可。
内容的提问来源于stack exchange,提问作者Forsythe
相关产品推荐
相关产品推荐

