You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

运行dbt Jinja模型时提示unexpected ')'语法错误如何解决

dbt对接Snowflake动态生成UNION语句报意外右括号语法错误

问题场景

  • 基于dbt对接Snowflake构建模型,通过Jinja语法遍历指定Schema下的表名列表,为每张表生成对应COPY_HISTORY查询语句
  • 预期逻辑:循环遍历每张表生成独立SQL片段,除最后一次循环外,片段之间用UNION拼接,实现批量查询所有表的近90天复制历史
  • 运行时抛出SQL编译错误,手动检查代码未发现明显多余括号:

11:44:23 001003 (42000): SQL compilation error
11:44:23 syntax error line 14 at position 6 unexpected ')'.

原始代码如下:

{{
config(
    query_tag = 'DBT: Staging_History'  
)
}}

{% set table_names_query %}
select table_name
from information_schema.tables 
where table_type = 'BASE TABLE' and TABLE_CATALOG = 'PROD_SOURCE' and TABLE_SCHEMA = 
'DIS_STG'
{% endset %}

{% set results = run_query(table_names_query) %}

{% if execute %}
{% set results_list = results.columns[0].values() %}
{% else %}
{% set results_list = [] %}
{% endif %}


{% for table_name in results_list %}
SELECT * FROM TABLE(INFORMATION_SCHEMA.copy_history(table_name=>'PROD_SOURCE.DIS_STG.DBO_ORGANIZATION', start_time=>dateadd(DAY, -90, current_timestamp)))
{% if not loop.last %} UNION {% endif %}
{% endfor %}

错误根因

  1. 核心逻辑bug:循环遍历results_list表名列表,但SELECT语句中COPY_HISTORY的table_name参数被硬编码为固定表DBO_ORGANIZATION,完全没有使用循环变量table_name,不符合批量查询所有表的预期。
  2. 语法错误直接诱因:
    • run_query调用放在了{% if execute %}判断外,dbt解析阶段(execute=False)不会实际执行查询,此时results_list为空数组,for循环不会输出任何SELECT语句,最终编译出的SQL为空。部分dbt版本和Snowflake驱动处理空SQL时会出现解析异常,抛出多余括号的报错。
    • 代码中如果存在转义后的箭头符号(比如从网页复制时带的=>而非标准=>),Snowflake解析器会将其识别为独立的=、>运算符,打乱表函数的参数结构,导致括号匹配失败。
  3. 性能隐患:使用UNION会对所有结果做全局去重,COPY_HISTORY查询场景下无去重必要,会造成不必要的算力浪费。

修复方案

调整代码结构,增加空结果兜底逻辑,修正动态表名拼接,替换为性能更好的UNION ALL,修复后代码如下:

{{
config(
    query_tag = 'DBT: Staging_History'  
)
}}

{% if execute %}
{% set table_names_query %}
select table_name
from information_schema.tables 
where table_type = 'BASE TABLE' 
  and TABLE_CATALOG = 'PROD_SOURCE' 
  and TABLE_SCHEMA = 'DIS_STG'
{% endset %}
{% set results = run_query(table_names_query) %}
{% set results_list = results.columns[0].values() %}
{% else %}
{% set results_list = [] %}
{% endif %}

{% if results_list | length > 0 %}
{% for table_name in results_list %}
SELECT * FROM TABLE(INFORMATION_SCHEMA.copy_history(
    table_name => 'PROD_SOURCE.DIS_STG.{{ table_name }}', 
    start_time => dateadd(DAY, -90, current_timestamp())
))
{% if not loop.last %} UNION ALL {% endif %}
{% endfor %}
{% else %}
-- 无匹配表时返回空结构,避免空SQL报错
SELECT 
    cast(null as varchar) as FILE_NAME,
    cast(null as varchar) as STAGE_LOCATION,
    cast(null as timestamp_ltz) as LAST_LOAD_TIME,
    cast(null as varchar) as STATUS,
    cast(null as int) as ROW_COUNT,
    cast(null as int) as ROW_PARSED
WHERE 1=0
{% endif %}

验证说明

  • 先执行dbt compile查看target目录下编译出的SQL,确认:
    • 无多余括号、特殊转义字符
    • 每张表的COPY_HISTORY参数中,表名已正确替换为查询到的表名
    • UNION ALL仅存在于两个SELECT片段之间,末尾无多余的UNION关键字
  • 如果使用低版本Snowflake不支持=>命名参数语法,可将COPY_HISTORY调用改为位置参数写法即可:
    INFORMATION_SCHEMA.copy_history('PROD_SOURCE.DIS_STG.{{ table_name }}', dateadd(DAY, -90, current_timestamp()))

内容的提问来源于stack exchange,提问作者Nick Hall

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 12:06:26