一、对 Hive 数据库(Database / Schema)的操作
在 Hive 里,Database 和 Schema 是同义词,本质是 HDFS 上的一个目录(默认在 /user/hive/warehouse/<db_name>.db),用于隔离表的命名空间。
1. 创建数据库
1
2
3
4
| CREATE DATABASE [IF NOT EXISTS] db_name
[COMMENT '数据库描述']
[LOCATION 'hdfs_path'] -- 自定义 HDFS 存储路径
[WITH DBPROPERTIES ('key1'='value1', ...)]; -- 附加元数据
|
示例:
1
2
3
4
| CREATE DATABASE IF NOT EXISTS tcec_dw
COMMENT 'TCEC 数据仓库'
LOCATION '/warehouse/tcec_dw.db'
WITH DBPROPERTIES ('owner'='liuyuhang', 'created'='2026-05-17');
|
显示更多行
📌 要点:不指定 LOCATION 时,默认路径为 hive.metastore.warehouse.dir 配置项 + db_name.db。
2. 查看数据库
| 操作 | 语法 | 说明 |
|---|
| 列出所有库 | SHOW DATABASES; | 类似 MySQL 的 SHOW DATABASES |
| 模糊匹配 | SHOW DATABASES LIKE 'tcec*'; | 支持通配符 |
| 查看库详情 | DESC DATABASE db_name; | 显示路径、owner |
| 查看完整信息 | DESC DATABASE EXTENDED db_name; | 额外显示 DBPROPERTIES |
| 查看当前库 | SELECT current_database(); | 当前会话所在库 |
3. 切换数据库
4. 修改数据库
Hive 数据库的元数据大部分不可修改(如名字、位置),只能改属性和 owner:
1
2
3
4
5
| ALTER DATABASE db_name SET DBPROPERTIES ('edited-by'='liu', 'createtime'='20260515');
ALTER DATABASE db_name SET OWNER USER liuyuhang;
ALTER DATABASE db_name SET LOCATION 'hdfs://new_path'; -- Hive 2.2.1+ 支持
|
5. 删除数据库
1
| DROP DATABASE [IF EXISTS] db_name [RESTRICT | CASCADE];
|
| 模式 | 行为 |
|---|
RESTRICT(默认) | 库里有表则报错,必须先清空 |
CASCADE | 级联删除库下所有表(⚠️ 生产慎用) |
1
| DROP DATABASE IF EXISTS tcec_dw CASCADE;
|
二、对数据表(Table)的操作
Hive 表分为:内部表(Managed Table)、外部表(External Table)、分区表(Partitioned)、分桶表(Bucketed)、临时表(Temporary)。
1. 创建表
1.1 完整建表语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name
( col_name data_type [COMMENT '列注释'], ... )
[COMMENT '表注释']
[PARTITIONED BY (col_name data_type, ...)] -- 分区
[CLUSTERED BY (col_name, ...)
[SORTED BY (col_name [ASC|DESC], ...)]
INTO num_buckets BUCKETS] -- 分桶
[ROW FORMAT DELIMITED -- 指定分隔符
FIELDS TERMINATED BY ','
COLLECTION ITEMS TERMINATED BY '|'
MAP KEYS TERMINATED BY ':'
LINES TERMINATED BY '\n'] -- 行格式
[STORED AS TEXTFILE | ORC | PARQUET | SEQUENCEFILE | AVRO] -- 存储格式
[LOCATION 'hdfs_path'] -- 数据位置
[TBLPROPERTIES ('key'='value', ...)]; -- 表属性
|
1.2 内部表 vs 外部表
| 维度 | 内部表(Managed) | 外部表(External) |
|---|
| 关键字 | CREATE TABLE | CREATE EXTERNAL TABLE |
| 数据归属 | Hive 管理数据生命周期 | Hive 仅管理元数据 |
DROP TABLE 影响 | 同时删除元数据 + HDFS 数据 | 只删元数据,HDFS 数据保留 |
| 典型场景 | 中间结果、临时数仓表 | ODS 层原始数据、共享数据 |
内部表
1
2
3
4
5
6
7
| CREATE TABLE dws_user_order (
user_id BIGINT COMMENT '用户ID',
order_cnt INT,
total_amt DECIMAL(18,2)
)
STORED AS ORC
TBLPROPERTIES ('orc.compress'='SNAPPY');
|
外部表(指向已存在的 HDFS 数据)
1
2
3
4
5
6
7
| CREATE EXTERNAL TABLE ods_user_log (
user_id BIGINT,
action STRING,
ts STRING
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
LOCATION '/data/raw/user_log/';
|
1.3 分区表
1
2
3
4
5
6
7
| CREATE TABLE dwd_order (
order_id BIGINT,
user_id BIGINT,
amount DECIMAL(18,2)
)
PARTITIONED BY (dt STRING, region STRING) -- 二级分区
STORED AS PARQUET;
|
在HDFS上,分区会体现为目录结构:
1
2
3
4
5
6
| /user/hive/warehouse/dwd_order/
├── dt=2024-05-17/
│ ├── region=Chengdu/
│ │ └── 000000_0 (Parquet文件)
│ ├── region=Beijing/
│ └── region=Shanghai/
|
📌 分区字段不出现在普通列中,它们的值是通过目录名体现的,本质是 HDFS 上的子目录:/dt=2026-05-17/region=cn/。
错误案例:
1
2
3
4
5
6
| -- 错误示例:CTAS缺少分区字段
CREATE TABLE employee_partitioned
PARTITIONED BY (dept STRING, year INT)
AS
SELECT id, name, salary -- 只有3个字段,但表需要5个字段(3个普通+2个分区)
FROM source_table;
|
报错信息(Hive 3.x):FAILED: SemanticException [Error 10044]: Line 1:18 Insert to partition columns requires all partition columns to be specified in the SELECT expression. Specified: id,name,salary; Missing: dept,year
原因:分区字段的值必须来自查询结果。Hive不知道如何为每条记录分配 dept='?'和 year=?
加载数据到分区
1
2
3
| LOAD DATA LOCAL INPATH '/path/to/data_china_0517.csv'
INTO TABLE dwd_order s
PARTITION (dt='2024-05-17', region='2024-05-17China');
|
查询分区表
查询时,在WHERE中使用分区字段作为条件,才能触发分区裁剪。
1
2
3
| -- 高效查询:只扫描‘China’分区下的‘2024-05-17’子目录
SELECT * FROM dwd_order
WHERE region='China' AND dt='2024-05-17';
|
查看分区
1
| show partitions dwd_order;
|
添加分区
添加分区的字段必须之前在表中存在否则会报错
1
2
3
4
| ALTER TABLE dwd_order ADD
PARTITION(dt='2024-05-17', region='Chengdu')
PARTITION(dt='2024-05-17', region='Beijing')
PARTITION(dt='2024-05-17', region='Shanghai');
|
删除分区
DROP PARTITION
1
2
3
4
5
6
7
8
9
10
| -- 这将删除 dt='2024-05-17' 下的所有region分区
ALTER TABLE dwd_order DROP PARTITION(dt='2024-05-17');
-- 或者删除多个指定分区
ALTER TABLE dwd_order DROP PARTITION(dt='2024-05-17', region='Chengdu'), PARTITION(dt='2024-05-17', region='Beijing');
-- 删除所有以特定模式匹配的分区
ALTER TABLE dwd_order DROP PARTITION(dt='2024-05-*');
ALTER TABLE dwd_order DROP PARTITION(region LIKE 'Cheng%');
|
TRUNCATE PARTITION
1
2
3
| -- TRUNCATE PARTITION: 只删除数据,保留分区结构
TRUNCATE TABLE dwd_order PARTITION(dt='2024-05-17');
-- 执行后,分区目录还存在,但里面的数据文件被清空
|
动态分区
动态分区 是指Hive在执行INSERT操作时,根据SELECT语句中某几列的值,自动决定数据应该写入哪个分区,而无需手动为每条数据指定分区值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| -- 静态分区:需要明确指定分区值
INSERT INTO sales PARTITION (dt='2024-05-17', region='Chengdu')
SELECT order_id, amount FROM source_table WHERE dt='2024-05-17' AND region='Chengdu';
-- 动态分区:分区值从数据中自动提取
INSERT INTO sales PARTITION (dt, region)
SELECT order_id, amount, dt, region FROM source_table;
-- 常用模式:一级静态分区 + 二级动态分区
INSERT OVERWRITE TABLE user_behavior PARTITION (dt='2024-05-17', province)
SELECT
user_id,
action,
province -- 动态分区字段
FROM source_table
WHERE dt = '2024-05-17';
-- 结果:在 dt=2024-05-17 下,按province自动创建子分区
|
动态分区的核心配置参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| -- 1. 启用动态分区(必须)
SET hive.exec.dynamic.partition=true;
-- 2. 设置分区模式(重要!)
SET hive.exec.dynamic.partition.mode=nonstrict;
-- strict: 必须至少有一个静态分区(安全但繁琐)
-- nonstrict: 允许所有分区都是动态的(生产常用)
-- 3. 控制最大分区数(防止创建过多分区)
SET hive.exec.max.dynamic.partitions=10000; -- 总共允许的最大动态分区数
SET hive.exec.max.dynamic.partitions.pernode=1000; -- 每个Mapper/Reducer允许的最大分区数
-- 4. 控制文件数量
SET hive.exec.max.created.files=100000; -- 整个作业允许创建的最大文件数
-- 5. 错误处理
SET hive.error.on.empty.partition=false; -- 是否在动态分区插入产生空分区时报错
|
1.4 分桶表
分桶的本质是在数据写入时,通过计算指定字段的哈希值,将哈希值相同的分到同一个桶(值相同则一定在同一个桶,但同一个桶的值不一定相同),每个桶对应一个文件。这需要读取、计算哈希、排序、写入等多个步骤。
分桶可以提高JOIN 查询和抽样效率,详见 Hive分桶.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| -- 1. 开启分桶约束(老版本需要,新版本Hive默认开启)
SET hive.enforce.bucketing = true;
-- 2. 设置Reduce任务数等于桶数,确保能生成对应数量的文件
SET mapreduce.job.reduces = 32; -- 注意:这里应该设置为32,与INTO 32 BUCKETS对应
CREATE TABLE dwd_user (
user_id BIGINT,
name STRING
)
-- 3. 核心定义:此表根据user_id的哈希值,将数据分散到32个桶文件中
CLUSTERED BY (user_id)
-- 4. 在每个桶文件内部,数据按照user_id升序排列
SORTED BY (user_id)
-- 5. 总共创建32个桶
INTO 32 BUCKETS
-- 6. 数据以ORC列式存储格式保存,以获得最佳的压缩和查询性能
STORED AS ORC;
|
📌 分桶字段必须是表中已有列,根据 hash(col) % num_buckets 分配到不同文件。配合你之前学的 Bucket Map Join / SMB Join 来优化大表 JOIN。
hdfs dfs -put 和 LOAD DATA INPATH都无法加载数据到分桶表,需要使用INSERT OVERWRITE
- hdfs dfs -put 的问题(纯文件移动)
1
2
| # 这只是简单的文件复制,Hive完全无法控制
hdfs dfs -put /local/user.txt /user/hive/warehouse/dwd_user/
|
问题所在:
你上传的文件可能包含多个用户的混合数据
Hive不知道如何将文件内容分配到32个桶中
文件格式不匹配(ORC格式需要特殊的二进制编码)
没有桶的元数据信息(如索引、统计信息)
结果:Hive会把这个文件当成一个普通文件,分桶功能完全失效。
- LOAD DATA 的问题(格式转换但无计算)
1
2
| -- 这只会移动和转换数据格式,但不会计算分桶
LOAD DATA INPATH '/input/user.txt' INTO TABLE dwd_user;
|
LOAD DATA实际上做了什么:
将HDFS上的源文件移动到表目录
如果格式不同,会做简单转换
但不会执行MapReduce计算
为什么不行:
没有对user_id计算哈希值
没有按哈希值分配数据到不同桶
没有在每个桶内排序
没有生成ORC的复杂结构(索引、统计信息等)
- INSERT OVERWRITE 为什么能行
1
2
3
| -- 这才是正确的分桶表数据加载方式
INSERT OVERWRITE TABLE dwd_user
SELECT user_id, name FROM source_table;
|
这个过程发生了什么(简化版):
1
2
3
4
5
6
7
8
| -- 1. 启动MapReduce/Spark作业
-- 2. Map阶段:读取源数据
-- 3. 为每行计算哈希:hash(user_id) % 32 → 得到桶编号(0-31)
-- 4. Reduce阶段:32个Reducer各自处理分配到的数据
-- - 每个Reducer处理一个桶的数据
-- - 在Reducer内部对user_id排序
-- - 以ORC格式写入到对应桶文件
-- 5. 结果:生成32个有序的ORC文件
|
1.5 CTAS & LIKE 建表
⚠️ CTAS 不能创建分区表/外部表,因为分区/外部属性需要显式声明。
2. 查看表
| 操作 | 语法 |
|---|
| 列出当前库所有表 | SHOW TABLES; |
| 模糊查询 | SHOW TABLES LIKE 'dwd_*'; |
| 查看表结构 | DESC table_name; |
| 查看详细信息 | DESC FORMATTED table_name; (生产高频:能看到存储路径、格式、压缩、行数等) |
| 查看建表语句 | SHOW CREATE TABLE table_name; |
| 查看分区 | SHOW PARTITIONS table_name; |
| 查看分区(过滤) | SHOW PARTITIONS table_name PARTITION(dt='2026-05-17'); |
3. 修改表(ALTER TABLE)
3.1 表级别修改
1
2
3
4
5
6
7
8
9
10
11
12
| -- 重命名
ALTER TABLE old_name RENAME TO new_name;
-- 修改表属性
ALTER TABLE t SET TBLPROPERTIES ('comment'='新描述');
-- 修改 SerDe
ALTER TABLE t SET SERDE 'org.apache.hadoop.hive.serde2.OpenCSVSerde';
-- 内外部表互转
ALTER TABLE t SET TBLPROPERTIES ('EXTERNAL'='TRUE'); -- 改外部表
ALTER TABLE t SET TBLPROPERTIES ('EXTERNAL'='FALSE'); -- 改内部表
|
3.2 列级别修改
1
2
3
4
5
6
7
8
| -- 增加列
ALTER TABLE t ADD COLUMNS (email STRING COMMENT '邮箱');
-- 修改列(改名、改类型、调整位置)
ALTER TABLE t CHANGE COLUMN old_col new_col STRING [AFTER other_col | FIRST];
-- 替换全部列(保留表,覆盖列定义,⚠️慎用)
ALTER TABLE t REPLACE COLUMNS (id BIGINT, name STRING);
|
⚠️ Hive 不支持直接 DROP COLUMN,要删列必须用 REPLACE COLUMNS 把保留的列重列一遍。
3.3 分区级别修改
1
2
3
4
5
6
7
8
9
10
11
| -- 添加分区(手动)
ALTER TABLE t ADD PARTITION (dt='2026-05-17', region='cn')
LOCATION '/data/t/dt=2026-05-17/region=cn';
-- 删除分区
ALTER TABLE t DROP IF EXISTS PARTITION (dt='2026-05-17');
-- 修改分区路径
ALTER TABLE t PARTITION (dt='2026-05-17') SET LOCATION 'hdfs://new_path';
-- 修复分区(HDFS 上有目录但元数据没注册时常用)
MSCK REPAIR TABLE t;
|
4. 删除/清空表
1
2
3
4
5
| -- 删除表(内部表会删 HDFS 数据,外部表只删元数据)
DROP TABLE [IF EXISTS] table_name;
-- 清空表(保留结构,删数据;仅对内部表生效)
TRUNCATE TABLE table_name [PARTITION (dt='2026-05-17')];
|
如果HDFS开启了回收站,drop的数据是可以恢复的,表结构恢复不了,需要自己创建
TRUNCATE清空的表不进回收站,无法恢复(慎用)
5. 数据加载
分区中加载数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| -- 从本地文件加载(复制)
LOAD DATA LOCAL INPATH '/home/liu/data.csv' INTO TABLE t;
-- 从 HDFS 加载(移动,原路径文件消失)
LOAD DATA INPATH '/tmp/data.csv' [OVERWRITE] INTO TABLE t PARTITION (dt='2026-05-17');
-- INSERT 查询写入
INSERT INTO TABLE t PARTITION (dt='2026-05-17') SELECT ... FROM src;
INSERT OVERWRITE TABLE t PARTITION (dt='2026-05-17') SELECT ... FROM src;
-- 创建表时加载查询结果数据
CREATE TABLE t AS SELECT ... FROM src;
-- 创建表时加载本地文件数据
CREATE EXTERNAL TABLE t (t_id, t_name)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LOCATION './teacher';
-- 根据已有表结构建表,并导入表数据
CREATE TABLE teacher2 like teacher;
EXPORT TABLE teacher TO '/export/teacher';
IMPORT TABLE teacher2 FROM '/export/teacher';
|
6. 数据导出
Hive本身不存储数据,它管理的是存储在HDFS(或对象存储)上数据的“元数据”。因此,数据导出本质是将数据文件从Hive表对应的位置复制或转换到其他位置。
导出方式主要分为三类:insert导出、Hadoop导出、Hive Shell 导出、Export导出。
insert导出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| -- 基本语法:导出整个表或查询结果(格式化导出)
INSERT OVERWRITE LOCAL DIRECTORY '/export/teacher'
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ',' -- 指定字段分隔符,例如CSV格式
STORED AS TEXTFILE
SELECT * FROM teacher;
-- 导出指定查询结果到本地
INSERT OVERWRITE LOCAL DIRECTORY '/export/dept_sales'
SELECT dept_id, sum(salary) as total_salary
FROM employee
GROUP BY dept_id;
-- 导出指定查询结果到HDFS(没有LOCAL关键字)
INSERT OVERWRITE DIRECTORY '/export/dept_sales'
SELECT dept_id, sum(salary) as total_salary
FROM employee
GROUP BY dept_id;
|
Hadoop导出到本地
dfs -get /export/servers/exporthive/000000_0 /export/servers/exporthive/local.txt;
Hive Shell 导出
hive -f/-e 执行语句或脚本 > file
hive -e "select * from myhive.score;" > /export/servers/exporthive/score.txt;
将插入的查询语句写入.hql文件,再执行文件
1
2
3
4
| -- export.hql
-- 可以包含多条SQL语句
USE myhive;
SELECT * FROM score;
|
1
| hive -f export_data.hql > /export/servers/exporthive/score.txt;
|
将插入的查询语句写入.sh文件,再执行文件
1
2
3
4
| #!/bin/bash
# export.sh
# 这是Shell脚本,内部调用Hive
hive -e "SELECT * FROM myhive.score;"
|
1
| hive -f export_data.sh > /export/servers/exporthive/score.txt;
|
Export导出到HDFS
1
| EXPORT TABLE score TO '/export/exporthive/score';
|
7. 命令对照速查表(数据库 vs 数据表)
| 维度 | 数据库(Database) | 数据表(Table) |
|---|
| 创建 | CREATE DATABASE | CREATE [EXTERNAL/TEMPORARY] TABLE |
| 查看列表 | SHOW DATABASES | SHOW TABLES |
| 查看详情 | DESC DATABASE [EXTENDED] | DESC [FORMATTED] / SHOW CREATE TABLE |
| 切换/使用 | USE db_name | (SELECT 时通过 db.table 引用) |
| 修改属性 | ALTER DATABASE ... SET DBPROPERTIES | ALTER TABLE ... SET TBLPROPERTIES |
| 改名 | ❌ 不支持 | ✅ ALTER TABLE ... RENAME TO |
| 改结构 | 仅改属性/owner/location | 增/改/换列、加减分区 |
| 删除 | DROP DATABASE [CASCADE] | DROP TABLE / TRUNCATE TABLE |
三、Hive的DQL查询语法
Hive 的 DQL(Data Query Language,数据查询语言)整体语法和标准 SQL 很相似,但因为底层是分布式计算(MapReduce/Tez/Spark),所以在排序、分桶采样、Join 等地方有自己的特色。
SELECT 完整语法骨架
1
2
3
4
5
6
7
8
9
10
| SELECT [ALL | DISTINCT] select_expr, select_expr, ...
FROM table_reference
[WHERE where_condition]
[GROUP BY col_list]
[HAVING having_condition]
[CLUSTER BY col_list
| [DISTRIBUTE BY col_list] [SORT BY col_list]
| [ORDER BY col_list]
]
[LIMIT [offset,] rows]
|
这里的 | 表示"或者"(互斥选择),也就是说这三种排序方式只能选其中一种,不能同时使用:
| 选项 | 写法 | 含义 |
|---|
| ① | CLUSTER BY col | 分发 + 排序(合二为一,同字段升序) |
| ② | DISTRIBUTE BY col1 SORT BY col2 | 按 col1 分发到不同 Reducer,再按 col2 在 Reducer 内排序(可以拆开用) |
| ③ | ORDER BY col | 全局排序(强制 1 个 Reducer) |
📌 [ ] 表示可选;| 表示三选一;DISTRIBUTE BY 和 SORT BY 内部各自也是可选的,但通常成对出现。
执行顺序(面试高频):
FROM → JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY / SORT BY → LIMIT
WHERE
WHERE < 是不包含null的,查询结果中没有deptno=null的情况
1
| SELECT * FROM employees WHERE deptno < 20;
|
| 运算符 | 含义 |
|---|
=, <>, !=, <, >, <=, >= | 比较 |
BETWEEN A AND B | 闭区间 |
IS [NOT] NULL | 空值判断 |
IN / NOT IN | 集合 |
LIKE | 通配符 % _ |
RLIKE / REGEXP | 正则匹配(Java 正则) |
AND / OR / NOT | 逻辑 |
GROUP BY
GROUP BY 使用group by则select后只能写分组的字段或聚合函数
WHERE vs HAVING:
| 区别点 | WHERE | HAVING |
|---|
| 过滤阶段 | 分组前 | 分组后 |
| 能否用聚合函数 | ❌ 不能 | ✅ 可以 |
| 操作对象 | 原始行 | 分组结果/聚合函数 |
1
2
3
4
5
6
| SELECT
province,
city,
COUNT(DISTINCT user_id) AS 活跃用户数
FROM user_behavior
GROUP BY province, city;
|
排序
SORT BY
SORT BY 非全局排序,在数据进入reducer前完成排序,不保证全局有序
1
2
3
| SELECT * FROM employees
DISTRIBUTE BY deptno
SORT BY sal DESC;
|
SORT BY 单独使用时,数据是随机分发到 Reducer 的,所以你无法控制"哪些数据进同一个 Reducer"。
而 DISTRIBUTE BY 就是用来控制分发规则(使用Hash算法)的,相当于 MapReduce 里的 Partitioner,
1
2
3
| Map1 ┐ ┌──→ Reducer1: 部门10的所有员工,按 sal 降序
Map2 ┼──hash──→ ├──→ Reducer2: 部门20的所有员工,按 sal 降序
Map3 ┘ └──→ Reducer3: 部门30的所有员工,按 sal 降序
|
CLUSTER BY
CLUSTER BY 是 DISTRIBUTE BY + SORT BY 的简写,但有两个限制:
分发字段 = 排序字段(必须同一个字段)
只能升序(不能 DESC)
1
2
3
| -- 以下两句等价
SELECT * FROM emp CLUSTER BY deptno;
SELECT * FROM emp DISTRIBUTE BY deptno SORT BY deptno;
|
ORDER BY
ORDER BY 对输入做全局排序,因此只有1个reducer,当规模较大时会耗时
1
2
3
| Map1 ┐
Map2 ┼──→ [单个 Reducer,所有数据汇总] → 输出 1 个全局有序文件
Map3 ┘
|
总结
| 子句 | 作用范围 | 是否全局有序 | Reduce 数量 | 使用场景 |
|---|
ORDER BY | 全局排序 | ✅ 是 | 强制为 1 | 小数据量全排序 |
SORT BY | 每个 Reducer 内部排序 | ❌ 否 | 多个 | 局部排序,输出多个有序文件 |
DISTRIBUTE BY | 控制数据到哪个 Reducer | ❌ 否 | 多个 | 类似 MR 中的 partitioner |
CLUSTER BY | = DISTRIBUTE BY + SORT BY(同字段、升序) | ❌ 否 | 多个 | 简写形式 |
JOIN
Join hive2 支持在ON·后连接 < > = OR,一个JOIN就会启动一个job,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| -- 表A: employees
+-----+-------+---------+
| id | name | dept_id |
+-----+-------+---------+
| 1 | Alice | 101 |
| 2 | Bob | 102 |
| 3 | Carol | NULL |
+-----+-------+---------+
-- 表B: departments
+-------+-----------+
| dept_id | dept_name |
+-------+-----------+
| 101 | Sales |
| 103 | IT |
+-------+-----------+
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| -- INNER JOIN 内连接(A ∩ B): 基于连接条件的交集,只返回满足条件的行
-- 等价于 SELECT employees, departments
SELECT e.name, d.dept_name
FROM employees e
INNER JOIN departments d ON e.dept_id = d.dept_id;
-- 返回
Alice Sales
-- LEFT [OUTER] JOIN 左外连接:左边数据会完全保留,右边符合连接条件的才会保留(右连接则相反)
SELECT e.name, d.dept_name
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.dept_id;
--返回
Alice Sales
Bob NULL
Carol NULL
-- FULL [OUTER] JOIN 全连接(左连接 ∪ 右连接 但要去除重复行):左表所有行 + 右表所有行,不匹配的行用NULL填充
SELECT e.name, d.dept_name
FROM employees e
FULL JOIN departments d ON e.dept_id = d.dept_id;
-- 返回
Alice Sales
Bob NULL
Carol NULL
NULL IT
-- 显式CROSS JOIN(笛卡尔积)左表×右表 = 全部组合
SELECT e.name, d.dept_name
FROM employees e
CROSS JOIN departments d;
-- 返回
Alice Sales
Alice IT
Bob Sales
Bob IT
Carol Sales
Carol IT
|
三种 Join 优化策略(联系上次聊的内容)
| Join 类型 | 触发条件 | 原理 |
|---|
| Common Join (Shuffle Join) | 默认 | Map 阶段输出 key,Reduce 阶段做 Join,开销大 |
| Map Join | 小表 < 25MB | 小表广播到每个 Mapper,无 Shuffle |
| Bucket Map Join / SMB Join | 两表分桶(+排序)且桶数成倍数 | 桶对桶 Join,无需 Shuffle |
LIMIT(带 offset)
1
2
3
| SELECT * FROM emp LIMIT 5; -- 前 5 行
SELECT * FROM emp LIMIT 10, 5; -- 跳过 10 行,取 5 行(部分版本支持)
|
UNION / UNION ALL
1
2
3
4
5
6
| -- UNION ALL:不去重,效率高
SELECT * FROM emp WHERE deptno = 10
UNION ALL
SELECT * FROM emp WHERE deptno = 20;
-- UNION:去重,会触发额外 MR
|