SELECT user_id, tag FROM user_tags
LATERAL VIEW explode(tags) t AS tag;
输出:
user_id
tag
1
vip
1
active
1
buyer
2
new_user
⚠️ 注意:用户 3 的空数组导致该行被丢弃。
统计每个标签的用户数
SELECT tag, COUNT(DISTINCT user_id) AS user_count
FROM user_tags
LATERAL VIEW explode(tags) t AS tag
GROUP BY tag
ORDER BY user_count DESC;
输出:
tag
user_count
vip
1
active
1
buyer
1
new_user
1
场景 2:保留空数组行(explode_outer)
问题
需要保留没有标签的用户,用于对比分析。
SQL 实现
SELECT user_id, tag FROM user_tags
LATERAL VIEW explode_outer(tags) t AS tag;
输出:
user_id
tag
1
vip
1
active
1
buyer
2
new_user
3
NULL
关键区别: 用户 3 被保留,tag 为 NULL。
场景 3:带索引展开(posexplode)
问题
需要知道标签在原数组中的位置(如优先级排序)。
SQL 实现
SELECT user_id, pos, tag FROM user_tags
LATERAL VIEW posexplode(tags) t AS pos, tag;
输出:
user_id
pos
tag
1
0
vip
1
1
active
1
2
buyer
2
0
new_user
索引从 0 开始,反映元素在原数组中的位置。
关联多个数组
-- 标签和分数按位置对应
SELECT
u.user_id,
t.tag,
s.score
FROM user_tags u
LATERAL VIEW posexplode(u.tags) t AS pos, tag
LATERAL VIEW posexplode(u.scores) s AS spos, score
WHERE t.pos = s.spos;
输出:
user_id
tag
score
1
vip
100
1
active
80
1
buyer
90
2
new_user
50
场景 4:多列数组同步展开(unnest)
问题
将键值对数组同步展开,保持对应关系。
SQL 实现
SELECT user_id, k, v FROM user_tags, unnest(tags, scores) AS t(k, v);
输出:
user_id
k
v
1
vip
100
1
active
80
1
buyer
90
2
new_user
50
优势: 比
posexplode
posexplode
+
WHERE
WHERE
更简洁,自动按位置配对。
场景 5:嵌套数组扁平化(flatten)
问题
将二维数组(如多个用户的标签合并后)扁平化为一维数组。
SQL 实现
SELECT flatten(ARRAY[ARRAY['vip', 'active'], ARRAY['buyer'], ARRAY['new_user']]) AS all_tags;
输出:
all_tags
vip:active:buyer:new_user
结合 explode 使用
-- 先扁平化再展开
SELECT tag FROM (
SELECT flatten(ARRAY[ARRAY['vip', 'active'], ARRAY['buyer']]) AS all_tags
)
LATERAL VIEW explode(all_tags) t AS tag;
输出:
tag
vip
active
buyer
场景 6:JSON 数组展开
问题
从 JSON 字符串中提取并展开数组字段。
SQL 实现
-- 需要先 PARSE_JSON 转为 JSON 类型
WITH parsed AS (
SELECT user_id, PARSE_JSON(metadata) AS meta FROM user_tags
)
SELECT user_id, tag FROM parsed
LATERAL VIEW explode_json_array_string(meta.tags) t AS tag;
输出:
user_id
tag
1
a
1
b
2
c
JSON 整数数组展开
WITH parsed AS (
SELECT user_id, PARSE_JSON(metadata) AS meta FROM user_tags
)
SELECT user_id, id FROM parsed
LATERAL VIEW explode_json_array_int(meta.ids) t AS id;
输出:
user_id
id
1
1
1
2
2
3
常见问题
1.
explode
explode
vs
explode_outer
explode_outer
的选择
-- explode: 空数组行被丢弃
SELECT user_id, tag FROM user_tags
LATERAL VIEW explode(tags) t AS tag;
-- 用户 3 不在结果中
-- explode_outer: 空数组行保留为 NULL
SELECT user_id, tag FROM user_tags
LATERAL VIEW explode_outer(tags) t AS tag;
-- 用户 3 在结果中,tag = NULL
2.
explode_json_array_*
explode_json_array_*
需要 JSON 类型
-- 错误: 直接传字符串
LATERAL VIEW explode_json_array_string('["a", "b"]') t AS tag
-- 正确: 先 PARSE_JSON
LATERAL VIEW explode_json_array_string(PARSE_JSON('["a", "b"]')) t AS tag
-- 索引是 0-based,不是 1-based
SELECT pos, tag FROM user_tags
LATERAL VIEW posexplode(tags) t AS pos, tag;
-- pos = 0, 1, 2 ...
5. 多次 LATERAL VIEW 的笛卡尔积
-- 错误: 两个 explode 会产生笛卡尔积
SELECT user_id, tag, score FROM user_tags
LATERAL VIEW explode(tags) t AS tag
LATERAL VIEW explode(scores) s AS score;
-- 用户 1 会产生 3 x 3 = 9 行
-- 正确: 用 unnest 或 posexplode + WHERE
SELECT user_id, k, v FROM user_tags, unnest(tags, scores) AS t(k, v);
性能优化建议
场景
优化策略
大数组展开
先用
filter
filter
缩小数组再展开
展开后聚合
在 LATERAL VIEW 后立即
WHERE
WHERE
过滤
JSON 数组展开
避免在展开前做复杂的 JSON 解析
多列展开
优先用
unnest
unnest
而非多个
posexplode
posexplode
+
WHERE
WHERE
-- 推荐: 先过滤再展开
SELECT user_id, tag FROM user_tags
LATERAL VIEW explode(filter(tags, t -> t != 'inactive')) t AS tag;
-- 不推荐: 展开后再过滤(产生更多中间行)
SELECT user_id, tag FROM user_tags
LATERAL VIEW explode(tags) t AS tag
WHERE tag != 'inactive';