基于布尔列过滤行并动态生成分组Union CTE的Dbt实现问题
问题解决:按flag分组生成带Union的CTE
场景与需求
我通过column的macro.Details为每张表动态生成SQL,表名存储在seed文件中,查询语句如下:
select table, columns, flag from {{ file }}
使用run_query执行上述查询并赋值给列表:
{%- set results = run_query(query) -%} {%- if execute -%} {%- set table_names,columns,flag = [results.columns[0],results.columns[1],results.columns[2]] -%} {%- endif -%}
需要根据flag列分组,生成两个带Union的CTE,期望效果:
flag_true as ( select * from tab1 union select * from tab2) , flag_false as ( select * from tab3 union select * from tab4)
遇到的问题
尝试了以下代码,但table_names循环包含两种flag值,导致末尾出现多余Union,且无法仅筛选flag == true的表:
flag_true as ( {% for tab_name in table_names %} {%- if flag[loop.index-1] %} select * from {{tab_name}} {% if not loop.last %}union{% endif %} {% endif %} {% endfor %} ),
解决方法
核心思路
先把对应flag的表名单独筛选出来,再对筛选后的列表循环生成Union语句,避免遍历全量表导致的loop.last判断错误。
修正后的代码
1. 先分组筛选表名
在execute块中,将表名与flag配对后拆分出两个分组:
{%- set results = run_query(query) -%} {%- if execute -%} {%- set table_flag_pairs = [] -%} {%- for row in results.rows -%} {%- do table_flag_pairs.append({"table": row[0], "flag": row[2]}) -%} {%- endfor -%} {%- set true_tables = [pair.table for pair in table_flag_pairs if pair.flag] -%} {%- set false_tables = [pair.table for pair in table_flag_pairs if not pair.flag] -%} {%- endif -%}
2. 基于筛选后的列表生成CTE
flag_true as ( {% for tab_name in true_tables %} select * from {{ tab_name }} {%- if not loop.last %} union {% endif %} {% endfor %} ), flag_false as ( {% for tab_name in false_tables %} select * from {{ tab_name }} {%- if not loop.last %} union {% endif %} {% endfor %} )
说明
- 通过循环
results.rows将表名与对应flag绑定,确保配对关系不混乱; - 用列表推导式分别提取
flag=true和flag=false的表名,得到纯净的分组列表; - 对分组列表循环时,
loop.last仅针对当前分组的最后一个表,不会生成多余的Union。
内容的提问来源于stack exchange,提问作者Coder
相关产品推荐
相关产品推荐

