4. Hive数据类型:基本数据类型、复杂数据类型、类型转换、数据类型的应用场景
聊到Hive,数据类型是绕不开的第一道坎。很多新手觉得这玩意儿就是定义字段时填个int、string就完事了。嗯,我刚开始也这么想,直到有一次在项目中因为类型搞错,跑了一整天的ETL任务全废了……从那以后,我对数据类型就格外上心。
说白了,Hive的数据类型设计,很大程度上借鉴了Java和SQL的思路。你如果写过Java,上手会很快。但Hive也有自己独特的地方,尤其是复杂数据类型,这是传统关系型数据库里不太常见的。
4.1 基本数据类型
基本数据类型,就是那些最基础、最常用的类型。我习惯把它们分成四类:数值型、字符串型、日期时间型、布尔型。
| 分类 | 类型 | 描述 | 示例 |
|---|---|---|---|
| 数值型 | TINYINT | 1字节整数,范围-128~127 | 100Y |
| SMALLINT | 2字节整数,范围-32768~32767 | 100S | |
| INT | 4字节整数,最常用 | 100 | |
| BIGINT | 8字节整数,处理大数时用 | 100L | |
| 浮点型 | FLOAT | 4字节单精度 | 3.14F |
| DOUBLE | 8字节双精度,默认浮点类型 | 3.14 | |
| DECIMAL | 高精度小数,适合金额 | DECIMAL(10,2) | |
| 字符串型 | STRING | 变长字符串,最常用 | 'hello' |
| VARCHAR | 定长字符串,Hive 0.12+ | VARCHAR(50) | |
| 日期时间 | DATE | 日期,格式yyyy-MM-dd | '2024-01-01' |
| TIMESTAMP | 时间戳,精确到纳秒 | '2024-01-01 12:00:00' | |
| 布尔型 | BOOLEAN | true/false | true |
4.2 复杂数据类型
复杂数据类型,这才是Hive的亮点。传统数据库一张表只能存扁平结构,但Hive允许你在一个字段里塞数组、字典甚至嵌套结构。你想想看,处理JSON日志或者多层嵌套的XML数据时,这有多方便。
4.2.1 ARRAY
数组,存储一组相同类型的元素。定义时用 ARRAY<类型>。
-- 建表
CREATE TABLE student_scores (
name STRING,
scores ARRAY<INT>
);
-- 插入数据
INSERT INTO student_scores VALUES ('张三', array(85, 90, 78));
-- 查询:用下标访问,下标从0开始
SELECT name, scores[0] AS first_score FROM student_scores;
-- 结果:张三, 85
4.2.2 MAP
键值对集合,类似Java的HashMap。定义时用 MAP<键类型, 值类型>。
-- 建表
CREATE TABLE employee_contact (
name STRING,
contact MAP<STRING, STRING>
);
-- 插入数据
INSERT INTO employee_contact VALUES
('李四', map('phone', '13800138000', 'email', 'lisi@example.com'));
-- 查询:用['key']访问
SELECT name, contact['phone'] AS phone FROM employee_contact;
我个人建议,MAP里的键尽量用STRING类型。用INT做键虽然可以,但可读性会差很多。你想想看,别人维护你的代码时,看到map[1]和map['dept_id'],哪个更清晰?
4.2.3 STRUCT
结构体,可以包含不同类型的字段。定义时用 STRUCT<字段名:类型, ...>。
-- 建表
CREATE TABLE user_info (
user_id INT,
address STRUCT<city:STRING, street:STRING, zip:INT>
);
-- 插入数据
INSERT INTO user_info VALUES
(1, named_struct('city', '北京', 'street', '长安街', 'zip', 100000));
-- 查询:用.访问字段
SELECT user_id, address.city FROM user_info;
4.2.4 UNIONTYPE
联合类型,可以存储多种不同类型中的一种。说实话,这个类型在实际项目中用得很少,我做了这么多年Hive,也就用过一两次。定义时用 UNIONTYPE<类型1, 类型2, ...>。
CREATE TABLE mixed_data (
id INT,
value UNIONTYPE<INT, STRING, FLOAT>
);
-- 插入时用create_union(标签, 值)
INSERT INTO mixed_data VALUES
(1, create_union(0, 100)), -- 标签0表示INT
(2, create_union(1, 'hello')), -- 标签1表示STRING
(3, create_union(2, 3.14)); -- 标签2表示FLOAT
4.3 类型转换
类型转换分两种:隐式转换和显式转换。说白了,隐式转换是Hive帮你自动转,显式转换是你自己用函数强制转。
4.3.1 隐式转换规则
Hive在以下情况会自动做类型提升:
- TINYINT → SMALLINT → INT → BIGINT → FLOAT → DOUBLE
- STRING → DOUBLE(如果字符串是数字格式)
- 所有类型都可以隐式转成STRING
-- 隐式转换示例
SELECT 1 + 2.5; -- 结果是3.5,INT自动转成DOUBLE
SELECT '100' > 50; -- 结果是true,STRING '100'转成INT 100
4.3.2 显式转换
用CAST函数强制转换:
-- 显式转换语法
CAST(expression AS type)
-- 示例
SELECT CAST('123' AS INT); -- 123
SELECT CAST(3.14159 AS INT); -- 3,直接截断小数
SELECT CAST('2024-01-01' AS DATE); -- 2024-01-01
-- 转换失败返回NULL
SELECT CAST('abc' AS INT); -- NULL
4.4 数据类型的应用场景
光知道类型定义还不够,关键是要知道什么时候用什么类型。我根据项目经验,总结了几条实用建议:
4.4.1 数值型选型
- ID字段:用INT或BIGINT。别用STRING存数字ID,查询性能差很多。
- 金额字段:必须用DECIMAL。FLOAT和DOUBLE有精度问题,算钱会出大事。
- 计数类:用INT就够了,除非你统计的是全国人口。
4.4.2 字符串型选型
- 普通文本:用STRING,长度不限,最灵活。
- 固定长度字段:比如身份证号、手机号,可以用VARCHAR(18)或VARCHAR(11),能节省一点存储。
- JSON或XML:用STRING存,配合get_json_object或json_tuple解析。
4.4.3 复杂数据类型选型
- 一对多关系:用ARRAY。比如一个学生有多门课的成绩。
- 键值对查询:用MAP。比如用户的各种属性配置。
- 多字段组合:用STRUCT。比如地址信息、坐标信息。
- 尽量避免:复杂类型嵌套超过2层,以及UNIONTYPE(除非你真的需要)。
4.4.4 日期时间选型
- 只需要日期:用DATE,占用4字节。
- 需要精确时间:用TIMESTAMP,占用8字节。
- 千万别:用STRING存日期。虽然也能用,但无法使用日期函数,而且排序会出问题。
嗯,关于Hive的数据类型,差不多就这些了。记住一句话:选类型时多花一分钟思考,后面能省一小时排错。我在项目中见过太多因为类型选错导致的线上事故了,希望你引以为戒。