Hive SQL 之 `LATERAL VIEW EXPLODE` 的正确打开方式
一文彻底搞懂 LATERAL VIEW EXPLODE
1. 引言
在处理复杂数据结构(如数组、映射)时,Hive SQL 提供了强大的功能来简化查询和数据分析。其中,LATERAL VIEW
和 EXPLODE
是两个特别有用的关键字,它们可以帮助我们将复杂的数据类型拆分成更易处理的行格式。本文将详细介绍 LATERAL VIEW EXPLODE
的概念、应用场景以及具体使用方法。
2. 概念定义
LATERAL VIEW
- 定义:
LATERAL VIEW
是 Hive SQL 中用于结合表生成函数(Table Generating Functions, TGFs)的一个关键字。它允许我们在同一个查询中对一个表执行额外的操作,例如调用EXPLODE
函数。 - 作用:通过
LATERAL VIEW
,我们可以为每个输入行生成多个输出行,并将这些新生成的行与原始数据合并,形成一个新的结果集。
EXPLODE
- 定义:
EXPLODE
是一种表生成函数,它接收一个数组或映射作为输入,并为输入中的每一个元素生成一行输出。 - 特点:如果输入是一个数组,则每一行代表数组中的一个元素;如果是映射,则每一行包含键值对中的一个键及其对应的值。
LATERAL VIEW EXPLODE
- 组合使用:
LATERAL VIEW EXPLODE
组合使用时,可以将复杂的数据结构(如数组或映射)转换成多行记录,使得原本难以处理的数据变得更加直观和易于分析。 - 好处:简化了对复杂数据类型的查询逻辑,提高了查询效率和灵活性。
3. 语法详解
语法结构
LATERAL VIEW [OUTER] EXPLODE(column) alias AS column_name
LATERAL VIEW
:这是必须的关键词,表示接下来要应用一个表生成函数。[OUTER]
:可选关键词。默认情况下,EXPLODE
只会处理非空数组或映射。如果你希望保留原表中的所有行,即使某些行的指定列为空或为 NULL,可以使用OUTER
关键词。EXPLODE(column)
:这是实际的表生成函数,column
表示你要展开的数组或映射列名。alias
:给由EXPLODE
操作产生的临时结果集起一个别名。这有助于在外层查询中引用这些新生成的行。AS column_name
:为EXPLODE
操作后的新列命名。这个新列将包含从原数组或映射中提取出来的单个元素。
示例语法
假设有一个表 users
,其中有一列 hobbies
是一个数组:
SELECT id, hobby
FROM users
LATERAL VIEW EXPLODE(hobbies) exploded_hobbies AS hobby;
在这个例子中:
LATERAL VIEW
表示我们将在当前查询上下文中引入一个表生成函数。EXPLODE(hobbies)
将hobbies
数组中的每个元素都转换成一行新的hobby
列。exploded_hobbies
是给由EXPLODE
操作产生的结果集起的别名。AS hobby
定义了新生成的列名为hobby
。
使用 OUTER
关键词
如果你想确保即使 hobbies
列为空或为 NULL 的行也保留在结果集中,可以使用 OUTER
关键词:
SELECT id, hobby
FROM users
LATERAL VIEW OUTER EXPLODE(hobbies) exploded_hobbies AS hobby;
4. 使用场景
LATERAL VIEW EXPLODE
在以下场景中尤为有用:
- 从数组或映射中提取信息:当你有一个包含多个值的数组或映射列,并希望将这些值展开成独立的行进行进一步分析时。
- 处理嵌套数据结构:对于存储在 JSON 或其他嵌套格式中的数据,可以通过
EXPLODE
来扁平化这些结构,便于后续操作。 - 聚合复杂数据:当需要对复杂数据类型中的元素进行统计或聚合时,
EXPLODE
可以帮助你将这些元素拆分出来,方便计算。
5. 实际应用案例
案例1:数组直接展开
假设我们有一个日志表 logs
,其中有一列 tags
存储的是用户标签的数组,如下所示:
id | tags |
---|---|
1 | [‘admin’, ‘user’] |
2 | [‘user’] |
我们想要知道每个标签出现的次数。这时就可以使用 LATERAL VIEW EXPLODE
:
SELECT tag, COUNT(*) as count
FROM logs
LATERAL VIEW EXPLODE(tags) exploded_tags AS tag
GROUP BY tag;
这段代码会将 tags
数组中的每个元素都转换成一行新的 tag
列,并统计每个标签的出现次数。结果可能如下:
tag | count |
---|---|
admin | 1 |
user | 2 |
案例2:先加工成数组,再展开
在实际的数据处理中,有时我们会遇到以逗号分隔的字符串(CSV 格式),例如一个用户的兴趣爱好可能被存储为一个逗号分隔的字符串。为了更好地分析这些数据,我们可以先使用 SPLIT
函数将字符串转换成数组,然后再用 LATERAL VIEW EXPLODE
将这个数组展开成多行。
假设我们有一个表 users
,其中有一列 interests
存储的是用户兴趣爱好的逗号分隔字符串,如下所示:
id | name | interests |
---|---|---|
1 | Alice | music,books |
2 | Bob | sports,games,books |
我们想要将每个用户的兴趣爱好单独列出,并统计每个兴趣爱好的出现次数。可以按照以下步骤进行操作:
步骤 1: 使用 SPLIT
函数生成数组
首先,我们需要将 interests
列中的逗号分隔字符串转换成数组。这可以通过 SPLIT
函数实现,该函数接收一个字符串和一个分隔符作为参数,并返回一个数组。
SELECT id, name, SPLIT(interests, ',') as interest_array
FROM users;
这段代码会将 interests
列中的每个逗号分隔字符串转换成一个数组 interest_array
。
步骤 2: 使用 LATERAL VIEW EXPLODE
展开数组
接下来,我们将使用 LATERAL VIEW EXPLODE
来将 interest_array
数组中的每个元素都转换成一行新的 interest
列。
SELECT id, name, interest
FROM (
SELECT id, name, SPLIT(interests, ',') as interest_array
FROM users
) t
LATERAL VIEW EXPLODE(interest_array) exploded_interests AS interest;
在这段代码中:
- 内层查询将
interests
列中的逗号分隔字符串转换成了数组interest_array
。 LATERAL VIEW EXPLODE(interest_array)
将数组中的每个元素都转换成一行新的interest
列。exploded_interests
是给由EXPLODE
操作产生的结果集起的别名。AS interest
定义了新生成的列名为interest
。
执行上述查询后,结果将会是:
id | name | interest |
---|---|---|
1 | Alice | music |
1 | Alice | books |
2 | Bob | sports |
2 | Bob | games |
2 | Bob | books |
步骤 3: 统计每个兴趣爱好的出现次数
最后,我们可以对展开后的兴趣爱好进行统计,计算每个兴趣爱好的出现次数:
SELECT interest, COUNT(*) as count
FROM (
SELECT id, name, SPLIT(interests, ',') as interest_array
FROM users
) t
LATERAL VIEW EXPLODE(interest_array) exploded_interests AS interest
GROUP BY interest;
这段代码会统计每个兴趣爱好的出现次数,结果可能是:
interest | count |
---|---|
music | 1 |
books | 2 |
sports | 1 |
games | 1 |
6. 操作层面
- 准备数据:确保你的表中包含至少一列是数组或映射类型,或者是一个逗号分隔的字符串。
- 编写查询:
- 如果是逗号分隔的字符串,使用
SPLIT
函数将其转换成数组。 - 使用
LATERAL VIEW
关键字引入EXPLODE
操作。 - 指定要展开的数组或映射列名。
- 使用
AS
关键字为新生成的列命名。
- 如果是逗号分隔的字符串,使用
- 执行查询:运行你的查询语句,查看由
EXPLODE
产生的新行。 - 分析结果:根据需要对生成的新行进行进一步的筛选、排序或聚合操作。
示例代码:
-- 将 users 表中 interests 字段的逗号分隔字符串转换成数组并展开成多行
SELECT id, name, interest
FROM (
SELECT id, name, SPLIT(interests, ',') as interest_array
FROM users
) t
LATERAL VIEW EXPLODE(interest_array) exploded_interests AS interest;
7. 常见问题与解决方案
-
问题:
LATERAL VIEW EXPLODE
后,原始列不见了怎么办?-
解决方案:在外层
SELECT
语句中明确指定你需要的所有列,包括原始列和新生成的列。你可以使用子查询别名来引用原始列,如t.interest
,还是来举个🌰:SELECT t.id, t.name, t.interest,exploded_interests.interest_new FROM ( SELECT id, name, SPLIT(interests, ',') as interest_array FROM users ) t LATERAL VIEW EXPLODE(t.interest_array) exploded_interests AS interest_new;
当然,当你explode后生成的字段和原始字段本身就不冲突时,可以忽略掉表名,简化如下:
SELECT t.id, t.name, interest, interest_new FROM ( SELECT id, name, SPLIT(interests, ',') as interest_array FROM users ) t LATERAL VIEW EXPLODE(t.interest_array) exploded_interests AS interest_new;
一般给出的示例代码都是下面这样的,这样在外层 SELECT 的时候,EXPLODE 新生成 interests 就会把内层的 interests 给覆盖掉,导致内层的 interests 不见了,这样主要是为了简化写法,从使用角度当然是没问题的,这个地方其实丢掉了内层原始 interests 字段;这个地方涉及到变量作用域的问题,展开说其实有点多,有心的同学这个地方可以稍微注意一下!!!
SELECT t.id, t.name, interest, interest_new FROM ( SELECT id, name, SPLIT(interests, ',') as interest_array FROM users ) subquery LATERAL VIEW EXPLODE(t.interest_array) subquery AS interests;
-
-
问题:如何处理空数组或 NULL 值?
- 解决方案:
EXPLODE
函数会跳过空数组或 NULL 值,不会产生任何新行。如果你需要保留这些行,可以在LATERAL VIEW
后添加OUTER
关键字,例如LATERAL VIEW OUTER EXPLODE(...)
。
- 解决方案:
8. 总结
LATERAL VIEW EXPLODE
是 Hive SQL 中处理复杂数据结构的强大工具,它能够将数组或映射等复杂类型转换成易于处理的行格式。通过本文的学习,你应该已经掌握了它的基本概念、应用场景及具体的使用方法。实践是最好的老师,尝试将这些知识应用于实际工作中,你会发现它极大地提升了数据处理的效率和灵活性。