一、Hive Map 是什么?
Hive 的 MAP 是一种复杂数据类型,用于在一个字段中保存多个 key-value 键值对,类似于 Java 的 Map 或 Python 的 dict。
key value
城市 北京
年龄 20
会员等级 VIP
Hive Map 的特点:
- 一个 Map 中包含多个键值对;
key和value类型必须明确;- 一个 Map 中的 key 通常要求唯一;
- 可以通过 key 访问对应的 value;
- 可以使用
explode()将 Map 拆成多行。
基本类型写法:
MAP<STRING, STRING>
MAP<STRING, INT>
MAP<STRING, DOUBLE>
例如:
MAP<STRING, STRING>
表示 key 和 value 都是字符串类型。
二、创建 Map
1. 使用 map() 函数
可以使用 map() 创建 Map:
SELECT MAP(
'city', 'Beijing',
'age', '20',
'level', 'VIP'
) AS attributes;
结果逻辑上是:
{
"city": "Beijing",
"age": "20",
"level": "VIP"
}
map() 函数的参数必须按照以下形式成对出现:
key1, value1, key2, value2, key3, value3
例如:
SELECT MAP(
'math', 90,
'english', 85
) AS scores;
这里创建的是:
MAP<STRING, INT>
2. 创建包含 Map 字段的表
CREATE TABLE user_attributes (
user_id BIGINT,
attributes MAP<STRING, STRING>
)
STORED AS ORC;
示例数据:
user_id | attributes
--------|-----------------------------------
1 | {"city":"Beijing","level":"VIP"}
2 | {"city":"Shanghai","level":"普通"}
如果 value 是数值类型:
CREATE TABLE user_scores (
user_id BIGINT,
scores MAP<STRING, INT>
)
STORED AS PARQUET;
示例:
user_id | scores
--------|--------------------------
1 | {"math":90,"english":85}
三、访问 Map 中的值
1. 使用下标访问
Hive Map 可以通过 key 访问 value:
SELECT
user_id,
attributes['city'] AS city,
attributes['level'] AS user_level
FROM user_attributes;
如果数据为:
attributes = {"city":"Beijing", "level":"VIP"}
结果为:
city | user_level
--------|-----------
Beijing | VIP
访问不存在的 key 时,通常返回 NULL:
SELECT attributes['不存在的字段']
FROM user_attributes;
2. 访问 Map 的所有 key
使用 map_keys() 获取所有 key:
SELECT
user_id,
MAP_KEYS(attributes) AS attribute_keys
FROM user_attributes;
结果逻辑上是:
user_id | attribute_keys
--------|----------------------
1 | [city, level]
返回结果是一个数组,即:
ARRAY<STRING>
3. 访问 Map 的所有 value
使用 map_values() 获取所有 value:
SELECT
user_id,
MAP_VALUES(attributes) AS attribute_values
FROM user_attributes;
结果逻辑上是:
user_id | attribute_values
--------|------------------
1 | [Beijing, VIP]
返回结果也是一个数组。
4. 判断 Map 是否包含某个 key
可以先获取 key 数组,再使用 array_contains():
SELECT
user_id,
ARRAY_CONTAINS(MAP_KEYS(attributes), 'city') AS has_city
FROM user_attributes;
也可以用于过滤:
SELECT user_id
FROM user_attributes
WHERE ARRAY_CONTAINS(MAP_KEYS(attributes), 'city');
不过,如果只是读取某个 key,更常见的方式是直接判断:
SELECT user_id
FROM user_attributes
WHERE attributes['city'] IS NOT NULL;
需要注意:如果 key 存在但 value 本身是 NULL,这种写法无法区分“key 不存在”和“value 为 NULL”。
四、Map 常用函数
1. size():获取键值对数量
SELECT
user_id,
SIZE(attributes) AS attribute_count
FROM user_attributes;
例如:
{"city":"Beijing", "level":"VIP"}
结果:
attribute_count = 2
对于 NULL Map,结果通常为 NULL;对于空 Map,长度为 0。
2. map_keys():获取所有 key
SELECT MAP_KEYS(attributes)
FROM user_attributes;
返回:
["city", "level"]
3. map_values():获取所有 value
SELECT MAP_VALUES(attributes)
FROM user_attributes;
返回:
["Beijing", "VIP"]
4. str_to_map():字符串转 Map
如果原始字段是一个字符串,例如:
city=Beijing,level=VIP,age=20
可以使用 str_to_map() 转换为 Map:
SELECT
STR_TO_MAP(
'city=Beijing,level=VIP,age=20',
',',
'='
) AS attributes;
参数含义:
第一个参数:原始字符串
第二个参数:键值对之间的分隔符
第三个参数:key 和 value 之间的分隔符
结果逻辑上是:
{
"city": "Beijing",
"level": "VIP",
"age": "20"
}
应用于表字段:
SELECT
user_id,
STR_TO_MAP(attribute_text, ',', '=') AS attributes
FROM user_attribute_text;
注意:str_to_map() 转换后通常是字符串类型的 Map:
MAP<STRING, STRING>
五、Map 转行:explode()
这是 Hive Map 最常见的用法之一。
原始数据:
user_id | attributes
--------|-----------------------------
1 | {"city":"Beijing","age":"20"}
SQL:
SELECT
user_id,
attr_key,
attr_value
FROM user_attributes
LATERAL VIEW EXPLODE(attributes) t AS attr_key, attr_value;
结果:
user_id | attr_key | attr_value
--------|----------|------------
1 | city | Beijing
1 | age | 20
数组使用 explode() 时通常展开成一个字段:
LATERAL VIEW EXPLODE(array_col) t AS item
Map 使用 explode() 时展开成两个字段:
LATERAL VIEW EXPLODE(map_col) t AS map_key, map_value
六、使用 LATERAL VIEW 展开 Map
explode() 是 UDTF,通常需要配合 LATERAL VIEW 使用。
基本格式:
SELECT
原表字段,
map_key,
map_value
FROM 表名
LATERAL VIEW EXPLODE(map_col) 别名 AS map_key, map_value;
完整示例:
SELECT
user_id,
k,
v
FROM user_attributes
LATERAL VIEW EXPLODE(attributes) t AS k, v;
执行逻辑可以理解为:
原始一行 Map
↓
拆分每个 key-value
↓
生成多行 key-value 数据
七、字符串字段转 Map 再展开
假设表中保存的是字符串:
user_id | attribute_text
--------|-----------------------------
1 | city=Beijing,level=VIP
2 | city=Shanghai,level=普通
可以先使用 str_to_map(),再使用 explode():
SELECT
user_id,
attr_key,
attr_value
FROM user_attribute_text
LATERAL VIEW EXPLODE(
STR_TO_MAP(attribute_text, ',', '=')
) t AS attr_key, attr_value;
结果:
user_id | attr_key | attr_value
--------|----------|------------
1 | city | Beijing
1 | level | VIP
2 | city | Shanghai
2 | level | 普通
这是处理键值对字符串的常见写法。
八、Map 与数组的区别
| 对比项 | ARRAY | MAP |
|---|---|---|
| 数据形式 | 有序元素集合 | key-value 键值对集合 |
| 访问方式 | arr[0] | map['key'] |
| 元素类型 | 所有元素通常同类型 | key 和 value 分别统一类型 |
| 是否有 key | 没有 | 有 |
| 展开结果 | 通常一列 | 通常两列:key、value |
| 常见函数 | size、explode、posexplode | size、map_keys、map_values、explode |
数组示例:
["java", "hive", "spark"]
Map 示例:
{"language":"java", "engine":"hive"}
简单理解:
ARRAY:通过位置找值
MAP:通过 key 找值
九、嵌套 Map
Hive 支持复杂类型嵌套,例如:
MAP<STRING, ARRAY<STRING>>
表示 Map 的 value 是数组:
{
"backend": ["java", "hadoop"],
"frontend": ["html", "css"]
}
建表示例:
CREATE TABLE user_skills (
user_id BIGINT,
skills MAP<STRING, ARRAY<STRING>>
)
STORED AS PARQUET;
先展开 Map:
SELECT
user_id,
skill_type,
skill_list
FROM user_skills
LATERAL VIEW EXPLODE(skills) t AS skill_type, skill_list;
再展开 value 数组:
SELECT
user_id,
skill_type,
skill
FROM user_skills
LATERAL VIEW EXPLODE(skills) t1 AS skill_type, skill_list
LATERAL VIEW EXPLODE(skill_list) t2 AS skill;
结果逻辑上是:
user_id | skill_type | skill
--------|------------|-------
1 | backend | java
1 | backend | hadoop
1 | frontend | html
1 | frontend | css
十、Map 中嵌套 Struct
也可以定义:
MAP<STRING, STRUCT<name:STRING, score:INT>>
例如:
{
"math": {"name":"数学", "score":90},
"english": {"name":"英语", "score":85}
}
建表示例:
CREATE TABLE student_scores (
student_id BIGINT,
scores MAP<STRING, STRUCT<subject_name:STRING, score:INT>>
)
STORED AS PARQUET;
展开并访问结构体字段:
SELECT
student_id,
subject,
score_info.subject_name AS subject_name,
score_info.score AS score
FROM student_scores
LATERAL VIEW EXPLODE(scores) t AS subject, score_info;
十一、Map 的实际应用
1. 用户动态属性
不同用户可能拥有不同属性,使用 Map 可以避免频繁增加字段:
{"city":"Beijing", "vip":"true", "source":"app"}
查询:
SELECT
user_id,
attributes['city'] AS city
FROM user_attributes;
2. 商品扩展属性
不同商品可能有不同属性:
{"color":"red", "memory":"256G", "screen":"6.7"}
3. 日志中的扩展参数
{"request_id":"abc123", "channel":"app", "version":"2.0"}
4. 指标名称和值
{"click":100, "view":1000, "order":20}
5. JSON 中的动态字段
对于字段名称不固定的 JSON 数据,可以先解析成 Map,再按 key 查询或展开。
十二、Map 使用示例:统计各属性值数量
原始表:
user_id | attributes
--------|-----------------------------
1 | {"city":"Beijing","level":"VIP"}
2 | {"city":"Beijing","level":"普通"}
3 | {"city":"Shanghai","level":"VIP"}
统计所有属性的 key-value 组合:
SELECT
attr_key,
attr_value,
COUNT(*) AS cnt
FROM user_attributes
LATERAL VIEW EXPLODE(attributes) t AS attr_key, attr_value
GROUP BY
attr_key,
attr_value;
结果逻辑上是:
attr_key | attr_value | cnt
---------|-------------|----
city | Beijing | 2
city | Shanghai | 1
level | VIP | 2
level | 普通 | 1
如果只统计城市,可以直接访问 Map:
SELECT
attributes['city'] AS city,
COUNT(*) AS user_count
FROM user_attributes
GROUP BY attributes['city'];
十三、Map 与 JSON 的区别
Map 和 JSON 都可以表示键值结构,但不是同一个概念。
JSON:一种数据交换格式,通常以字符串形式存在
MAP:Hive 的内部复杂数据类型
例如 JSON 字符串:
{"city":"Beijing","age":"20"}
它可能只是一个 STRING 字段,不能直接按照 Map 下标访问。
如果字段是普通 JSON 字符串,可以使用 JSON 函数:
SELECT
GET_JSON_OBJECT(json_text, '$.city') AS city
FROM user_json;
如果是键值对字符串:
city=Beijing,age=20
可以使用:
SELECT
STR_TO_MAP(attribute_text, ',', '=')['city'] AS city
FROM user_attribute_text;
两者区别如下:
| 数据形式 | 常用处理方式 |
|---|---|
| Hive Map 字段 | map_col['key'] |
| JSON 字符串 | get_json_object()、json_tuple() |
| 分隔符键值字符串 | str_to_map() |
十四、Map 使用时的注意事项
1. Key 和 value 类型要统一
例如:
MAP<STRING, STRING>
MAP<STRING, INT>
不能让同一个 Map 的 value 同时保存完全不同的数据类型。
2. 注意 key 不存在的情况
attributes['unknown_key']
如果 key 不存在,通常返回 NULL。
3. 注意 NULL 和空字符串
以下情况含义不同:
NULL:没有 Map
{}:Map 存在,但没有键值对
{"city":""}:city 存在,但值为空字符串
{"city":NULL}:city 存在,但值为空
业务处理时需要区分这些情况。
4. explode 会增加数据量
一个 Map 有 20 个键值对,展开后通常会生成 20 行。
100 万行 × 每行 20 个键值对
≈ 2000 万行输出
展开前要评估数据规模。
5. Map 不适合完全替代明确字段
如果业务字段固定且经常查询,例如:
user_id
register_date
province
user_level
最好直接设计成明确的列,而不是全部放到 Map 中。
Map 更适合:
- 字段动态变化;
- 属性种类不固定;
- 扩展字段较多;
- 低频查询的附加属性。
6. 复杂类型适合使用列式存储
Map 通常建议保存为:
Parquet
ORC
相比 CSV 和普通文本,列式格式通常更适合压缩、查询和复杂类型读取。
十五、快速记忆
创建 Map
MAP('city', 'Beijing', 'level', 'VIP')
访问 Map 的值
map_col['key']
获取所有 key
MAP_KEYS(map_col)
获取所有 value
MAP_VALUES(map_col)
获取键值对数量
SIZE(map_col)
字符串转 Map
STR_TO_MAP(str, ',', '=')
Map 转多行
LATERAL VIEW EXPLODE(map_col) t AS map_key, map_value
Map 中嵌套数组并展开
LATERAL VIEW EXPLODE(map_col) t1 AS k, array_value
LATERAL VIEW EXPLODE(array_value) t2 AS item
总结
Hive Map 用于在一列中保存多个键值对,核心操作可以概括为:
创建 Map:MAP()
访问值:map_col['key']
获取所有 key:MAP_KEYS()
获取所有 value:MAP_VALUES()
获取大小:SIZE()
字符串转 Map:STR_TO_MAP()
Map 转多行:EXPLODE()
最常用的两个场景是:
-- 直接根据 key 取值
SELECT attributes['city']
FROM user_attributes;
-- 将 Map 拆成多行 key-value
SELECT
user_id,
attr_key,
attr_value
FROM user_attributes
LATERAL VIEW EXPLODE(attributes) t AS attr_key, attr_value;
可以把 Hive Map 理解为:用一列保存一组动态属性,再根据 key 直接取值,或者展开成标准的多行键值明细。

810

被折叠的 条评论
为什么被折叠?



