Hive中EXPLODE、POSEXPLODE、COLLECT_SET详解

我们来深入探讨一下 Hive 中这三个非常实用但又功能迥异的函数:EXPLODE()POSEXPLODE()COLLECT_SET()。它们分别代表了“行转列”和“列转行”两种经典的数据处理场景。


1. EXPLODE() - 爆炸函数(行转列)

EXPLODE() 是 Hive 中最著名的 UDTF(User-Defined Table-Generating Function)。它的核心作用是将一个复杂的 数组(Array)映射(Map) 结构“炸开”,转换成多行数据,即所谓的“行转列”。

语法
SELECT EXPLODE(column_name) AS exploded_column FROM table_name;
  • column_name: 必须是 ARRAYMAP 类型的数据类型。
  • exploded_column: 炸开后的列别名。如果原始是数组,该列数据类型与数组元素相同;如果是 Map,则会生成 keyvalue 两列。
工作原理与示例

a) 炸开数组(ARRAY)

假设有一张表 student_courses,存储了学生和其选修的课程。

student_idnamecourses
1Alice[“Math”, “Physics”]
2Bob[“History”]

我们想将每个课程变成单独的一行。

SELECT student_id, name, exploded_course
FROM student_courses
LATERAL VIEW EXPLODE(courses) exploded_table AS exploded_course;

结果:

student_idnameexploded_course
1AliceMath
1AlicePhysics
2BobHistory

b) 炸开映射(MAP)

假设有一张表 product_features,存储了产品及其特性的键值对。

product_idfeatures
1001{“color”: “red”, “size”: “10cm”}
1002{“color”: “blue”}
SELECT product_id, exploded_key, exploded_value
FROM product_features
LATERAL VIEW EXPLODE(features) exploded_table AS exploded_key, exploded_value;

结果:

product_idexploded_keyexploded_value
1001colorred
1001size10cm
1002colorblue

关键点:

  • 必须与 LATERAL VIEW 一起使用EXPLODE() 本身会生成一个虚拟表,LATERAL VIEW 的作用是将这个虚拟表的结果和原始表的每一行进行关联(笛卡尔积),从而得到最终结果。
  • UDTF 不能直接出现在 SELECT 后与其他字段并列:例如 SELECT name, EXPLODE(courses) ... 是错误的语法,必须通过 LATERAL VIEW

2. POSEXPLODE() - 带位置的爆炸函数

POSEXPLODE()EXPLODE() 的增强版。它在炸开数组的同时,还返回数组元素的索引(位置)。这对于需要保留元素原始顺序的场景非常有用。

语法与示例

继续使用 student_courses 表。

SELECT student_id, name, pos, course
FROM student_courses
LATERAL VIEW POSEXPLODE(courses) exploded_table AS pos, course;

结果:

student_idnameposcourse
1Alice0Math
1Alice1Physics
2Bob0History

关键点:

  • 索引从 0 开始。
  • EXPLODE() 一样,必须与 LATERAL VIEW 联用。
  • 对于 MAP 类型,POSEXPLODE() 的行为与 EXPLODE() 一致,因为 Map 的键本身是无序的,返回位置意义不大(Hive 中 Map 的键序不确定)。

3. COLLECT_SET() - 聚合函数(列转行)

COLLECT_SET() 的作用与 EXPLODE() 完全相反。它是一个 聚合函数(UDAF),用于将分组内的多个数据收集起来,形成一个不重复(去重)的集合(Set),即数组(ARRAY)。这实现了“列转行”。

语法
SELECT grouping_column, COLLECT_SET(target_column) 
FROM table_name 
GROUP BY grouping_column;
  • target_column: 需要被聚合的列,可以是任意基本数据类型。
  • 返回值ARRAY 类型,且元素唯一
工作原理与示例

假设有一张订单明细表 orders,记录了每个用户的订单。

user_idorder_id
AO1
AO2
AO1
BO3

我们想找出每个用户拥有的所有唯一的订单 ID。

SELECT user_id, COLLECT_SET(order_id) AS unique_orders
FROM orders
GROUP BY user_id;

结果:

user_idunique_orders
A[“O1”, “O2”]
B[“O3”]
变体:COLLECT_LIST()

COLLECT_LIST() 函数与 COLLECT_SET() 功能几乎完全相同,唯一的区别是:它不去重,保留所有元素,包括重复项,并且不保证元素的顺序(在 Hive 的某些版本中,顺序可能是按数据插入的物理顺序)。

使用上面的例子:

SELECT user_id, COLLECT_LIST(order_id) AS all_orders
FROM orders
GROUP BY user_id;

结果:

user_idall_orders
A[“O1”, “O2”, “O1”]
B[“O3”]

关键点:

  • 是一个聚合函数,必须与 GROUP BY 子句一起使用。
  • COLLECT_SET() 保证结果数组中的元素是唯一的。
  • COLLECT_LIST() 保留所有原始值,允许重复。

总结与对比

特性EXPLODE() / POSEXPLODE()COLLECT_SET() / COLLECT_LIST()
类型UDTF (表生成函数)UDAF (聚合函数)
功能行转列:将一行中的复杂数据类型展开成多行列转行:将多行的值聚合成一个数组(一行)
数据量输出行数 >= 输入行数(通常增加)输出行数 <= 输入行数(通过分组减少)
关键子句LATERAL VIEWGROUP BY
去重N/A(原样展开)COLLECT_SET() 会去重,COLLECT_LIST() 不会

经典组合使用场景

一个常见的 ETL 模式是先用 EXPLODE 将数据打平进行处理,最后再用 COLLECT_SET 重新聚合。

场景:统计每个用户看过的所有不同的电影标签。

原始表 user_movie_tags:

user_idmovie_tags
1[“comedy”, “action”]
1[“action”, “sci-fi”]
2[“drama”]

步骤

  1. 先用 EXPLODE 打平,让每个标签成为一行。
  2. 然后按用户和标签分组(去重)。
  3. 最后按用户分组,用 COLLECT_SET 收集所有唯一标签。
WITH exploded_data AS (
    SELECT user_id, exploded_tag
    FROM user_movie_tags
    LATERAL VIEW EXPLODE(movie_tags) t AS exploded_tag
),
distinct_tags AS (
    SELECT user_id, exploded_tag AS tag
    FROM exploded_data
    GROUP BY user_id, exploded_tag -- 这一步根据业务逻辑,有时可省略
)
SELECT user_id, COLLECT_SET(tag) AS all_unique_tags
FROM distinct_tags
GROUP BY user_id;

最终结果:

user_idall_unique_tags
1[“comedy”, “action”, “sci-fi”]
2[“drama”]

通过这三个函数的灵活运用,可以高效地处理 Hive 中复杂的结构化数据。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值