一、explode是什么?
Hive 中的explode用于把一行中的数组或 Map 拆成多行。
一行数组 ↓ explode 多行普通字段例如原始数据:
user_id | tags --------|------------------ 1 | ["hive", "spark"] 2 | ["flink", "java"]展开后:
user_id | tag --------|------ 1 | hive 1 | spark 2 | flink 2 | java二、展开数组
Hive 中通常需要配合LATERAL VIEW使用:
SELECTuser_id,tagFROMuser_tags LATERALVIEWEXPLODE(tags)tAStag;其中:
EXPLODE(tags):展开 tags 数组 t :虚拟表别名 AS tag :定义展开后的字段名如果一行数组中有 3 个元素,该行通常会变成 3 行。
三、完整示例
1. 建表示例
CREATETABLEuser_tags(user_idBIGINT,tags ARRAY<STRING>)STOREDASORC;2. 查询数组元素
SELECTuser_id,tagFROMuser_tags LATERALVIEWEXPLODE(tags)tAStag;3. 展开后统计每个标签的用户数
SELECTtag,COUNT(DISTINCTuser_id)ASuser_countFROMuser_tags LATERALVIEWEXPLODE(tags)tAStagGROUPBYtag;注意:如果同一个用户的数组中重复出现同一个标签,直接COUNT(*)可能会重复计数;统计用户数时通常使用COUNT(DISTINCT user_id)。
四、字符串转数组后再展开
如果字段不是数组,而是逗号分隔的字符串:
user_id | tags --------|---------------- 1 | hive,spark,flink 2 | java,hadoop可以先用split()转成数组,再使用explode():
SELECTuser_id,tagFROMuser_tags LATERALVIEWEXPLODE(SPLIT(tags,','))tAStag;结果:
user_id | tag --------|------- 1 | hive 1 | spark 1 | flink 2 | java 2 | hadoop如果分隔符是竖线,由于split的分隔符是正则表达式,需要转义:
LATERALVIEWEXPLODE(SPLIT(tags,'\\|'))tAStag;五、展开 Map
explode()也可以展开 Map,结果会得到两个字段:key和value。
原始数据:
user_id | attributes --------|----------------------------- 1 | {"city":"Beijing","age":"20"}SQL:
SELECTuser_id,attr_key,attr_valueFROMuser_attributes LATERALVIEWEXPLODE(attributes)tASattr_key,attr_value;结果:
user_id | attr_key | attr_value --------|----------|------------ 1 | city | Beijing 1 | age | 20六、posexplode:展开并保留下标
如果除了元素值,还需要知道元素在数组中的位置,可以使用posexplode():
SELECTuser_id,pos,tagFROMuser_tags LATERALVIEWPOSEXPLODE(tags)tASpos,tag;结果:
user_id | pos | tag --------|-----|------ 1 | 0 | hive 1 | 1 | spark 2 | 0 | flink 2 | 1 | javaHive 数组下标从0开始。
常见用途:
- 保留数组原始顺序;
- 获取元素所在位置;
- 两个数组按照相同下标进行匹配。
七、两个数组按位置对应
假设表中有:
user_id | subjects | scores --------|------------------|-------- 1 | [math, english] | [90, 85]目标是得到:
user_id | subject | score --------|---------|------ 1 | math | 90 1 | english | 85可以分别使用posexplode(),再根据位置关联:
WITHsubject_dataAS(SELECTuser_id,pos,subjectFROMuser_score LATERALVIEWPOSEXPLODE(subjects)tASpos,subject),score_dataAS(SELECTuser_id,pos,scoreFROMuser_score LATERALVIEWPOSEXPLODE(scores)tASpos,score)SELECTs.user_id,s.subject,c.scoreFROMsubject_data sJOINscore_data cONs.user_id=c.user_idANDs.pos=c.pos;不要直接对两个数组分别explode后放在同一条查询中,否则容易产生笛卡尔积。
八、LATERAL VIEW OUTER EXPLODE
普通的explode()遇到NULL或空数组时,可能不会保留原始行:
SELECTuser_id,tagFROMuser_tags LATERALVIEWEXPLODE(tags)tAStag;如果希望数组为空时仍保留用户记录,可以使用OUTER:
SELECTuser_id,tagFROMuser_tags LATERALVIEWOUTEREXPLODE(tags)tAStag;没有元素的用户会保留,展开后的tag通常为NULL。
两者区别:
LATERAL VIEW EXPLODE:没有元素时可能不输出该行 LATERAL VIEW OUTER EXPLODE:没有元素时仍保留原始行九、展开数组中的 Struct
如果数组元素是结构体,例如:
orders = [ {product: "apple", price: 5}, {product: "banana", price: 8} ]表结构:
CREATETABLEuser_orders(user_idBIGINT,orders ARRAY<STRUCT<product:STRING,price:DOUBLE>>)STOREDASPARQUET;展开并访问 Struct 字段:
SELECTuser_id,order_item.productASproduct,order_item.priceASpriceFROMuser_orders LATERALVIEWEXPLODE(orders)tASorder_item;十、常见注意事项
1.explode会放大数据量
100 万行 × 每行 100 个数组元素 ≈ 1 亿行输出使用前要评估数据量和下游计算成本。
2. 注意NULL、空数组和空字符串
这几种情况含义不同:
NULL:没有数组值 []:数组存在,但没有元素 ['']:数组中有一个空字符串必要时可以过滤空值:
SELECTuser_id,tagFROMuser_tags LATERALVIEWEXPLODE(tags)tAStagWHEREtagISNOTNULLANDtag<>'';3. 展开后再聚合可能重复计数
展开会改变行数,后续计算指标时要重新确认统计口径。
4.explode通常不能直接放在普通SELECT中
推荐写法:
SELECTid,itemFROMtable_name LATERALVIEWEXPLODE(array_col)tASitem;而不是简单写成:
-- 某些 Hive 版本或场景下不适用SELECTid,EXPLODE(array_col)FROMtable_name;十一、快速记忆
-- 展开数组LATERALVIEWEXPLODE(array_col)tASitem-- 展开数组并保留下标LATERALVIEWPOSEXPLODE(array_col)tASpos,item-- 展开 MapLATERALVIEWEXPLODE(map_col)tASmap_key,map_value-- 数组为空时保留原始行LATERALVIEWOUTEREXPLODE(array_col)tASitem一句话总结:
explode用于把 Hive 一行中的数组或 Map 拆成多行;数组展开后得到元素,Map 展开后得到键和值,若需要保留下标则使用posexplode。