Hive map巨详细讲解

一、Hive Map 是什么?

Hive 的 MAP 是一种复杂数据类型,用于在一个字段中保存多个 key-value 键值对,类似于 Java 的 Map 或 Python 的 dict

key        value
城市        北京
年龄        20
会员等级    VIP

Hive Map 的特点:

  • 一个 Map 中包含多个键值对;
  • keyvalue 类型必须明确;
  • 一个 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 与数组的区别

对比项ARRAYMAP
数据形式有序元素集合key-value 键值对集合
访问方式arr[0]map['key']
元素类型所有元素通常同类型key 和 value 分别统一类型
是否有 key没有
展开结果通常一列通常两列:key、value
常见函数sizeexplodeposexplodesizemap_keysmap_valuesexplode

数组示例:

["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 直接取值,或者展开成标准的多行键值明细。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鸭梨山大哎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值