在不支持递归SQL的Hive/Impala中获取父节点所有后代
嗨,这个问题我在实际工作里碰过好多次了——毕竟Hive和Impala确实不支持标准SQL里的递归CTE,得换个接地气的思路来解决。先看看你的场景:是一条链式的层级结构,a→b→c→d→e→f→x,要把每个父节点对应的所有后代都列出来对吧?下面给你两种实用的方案,按需选就行:
方案一:固定层级用多层JOIN(简单直接)
如果你的层级数量是明确的(比如这个例子里最多6层嵌套),直接用多层JOIN+UNION ALL就能搞定,不用额外开发任何函数。
首先先确认源表结构(假设你的表名叫hierarchy):
CREATE TABLE IF NOT EXISTS hierarchy ( parent STRING, child STRING ) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t';
然后用CTE分层拼接所有后代:
WITH level1 AS ( -- 直接子节点 SELECT parent, child FROM hierarchy ), level2 AS ( -- 孙子节点:level1的子节点的子节点 SELECT l1.parent, l2.child FROM level1 l1 JOIN hierarchy l2 ON l1.child = l2.parent ), level3 AS ( -- 曾孙节点 SELECT l2.parent, l3.child FROM level2 l2 JOIN hierarchy l3 ON l2.child = l3.parent ), level4 AS ( SELECT l3.parent, l4.child FROM level3 l3 JOIN hierarchy l4 ON l3.child = l4.parent ), level5 AS ( SELECT l4.parent, l5.child FROM level4 l4 JOIN hierarchy l5 ON l4.child = l5.parent ), level6 AS ( SELECT l5.parent, l6.child FROM level5 l5 JOIN hierarchy l6 ON l5.child = l6.parent ) -- 把所有层级的结果合并,就是每个父节点的所有后代 SELECT parent, child FROM level1 UNION ALL SELECT parent, child FROM level2 UNION ALL SELECT parent, child FROM level3 UNION ALL SELECT parent, child FROM level4 UNION ALL SELECT parent, child FROM level5 UNION ALL SELECT parent, child FROM level6 ORDER BY parent, child;
执行后就能得到你要的结果:比如a对应的b、c、d、e、f、x都会被列出来,其他父节点的后代也一样。
方案二:层级不确定?用脚本迭代或UDTF(灵活通用)
如果你的层级是动态的,比如有时候嵌套10层,有时候20层,多层JOIN就太麻烦了,这时候可以用两种方式:
方法1:Shell脚本+临时表迭代
用脚本循环往临时表里插入新的后代,直到没有新数据为止:
- 先创建存储最终结果的临时表:
CREATE TABLE IF NOT EXISTS all_descendants ( parent STRING, descendant STRING ) STORED AS ORC;
- 初始化插入直接子节点:
INSERT INTO all_descendants SELECT parent, child FROM hierarchy;
- 写个Shell脚本循环迭代(比如叫
run_hierarchy.sh):
#!/bin/bash # 初始化计数 prev_count=$(hive -e "SELECT COUNT(*) FROM all_descendants" | tail -1) while true; do # 插入新的后代:已有的后代的子节点,且不在结果表里的 hive -e " INSERT INTO all_descendants SELECT a.parent, b.child FROM all_descendants a JOIN hierarchy b ON a.descendant = b.parent WHERE NOT EXISTS ( SELECT 1 FROM all_descendants c WHERE c.parent = a.parent AND c.descendant = b.child ); " # 获取当前计数 curr_count=$(hive -e "SELECT COUNT(*) FROM all_descendants" | tail -1) # 如果计数没变化,说明没有新数据了,退出循环 if [ "$curr_count" -eq "$prev_count" ]; then break fi # 更新前一次计数 prev_count=$curr_count done # 查询最终结果 hive -e "SELECT parent AS Parent, descendant AS Child FROM all_descendants ORDER BY parent, descendant;"
运行这个脚本就能自动遍历所有层级的后代,不管嵌套多少层都能处理。
方法2:自定义UDTF(用户定义表生成函数)
如果你的环境允许开发自定义函数,可以写一个Java UDTF,接收父节点和子节点,递归遍历所有后代。核心逻辑是把层级结构存到内存里(比如用Map<父节点, 子节点列表>),然后对每个父节点递归输出所有后代。
不过这个需要一点Java开发基础,大概步骤是:
- 继承Hive的
GenericUDTF类; - 在
initialize方法里定义输入输出参数; - 在
process方法里构建层级映射; - 在
close方法里递归遍历每个父节点的后代并输出。
这种方法适合需要频繁处理层级结构的场景,一次开发多次复用。
总结一下:如果层级少且固定,优先用方案一;如果层级动态不确定,用方案二的脚本或UDTF就行。
内容的提问来源于stack exchange,提问作者SK15




