Hadoop学习教程,从入门到精通, Hive数据仓库 — 完整知识点与案例代码(7)
·
Hive数据仓库 — 完整知识点与案例代码
一、数据仓库简介
1.1 什么是数据仓库
数据仓库(Data Warehouse)是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合,用于支持管理决策。
四大特征:
| 特征 | 说明 |
|---|---|
| 面向主题 | 围绕特定业务主题组织数据(如销售、客户、财务) |
| 集成性 | 将多个异构数据源统一整合 |
| 稳定性 | 数据加载后一般不修改、不删除 |
| 时变性 | 记录历史变化,支持趋势分析 |
1.2 数据仓库系统结构
数据源层 → 数据抽取/转换/加载(ETL) → 数据存储层 → 数据服务层 → 数据应用层
┌─────────────────────────────────────────────────┐
│ 数据应用层 │
│ (报表、BI工具、数据挖掘、即席查询) │
├─────────────────────────────────────────────────┤
│ 数据服务层 │
│ (OLAP、查询引擎、API) │
├─────────────────────────────────────────────────┤
│ 数据存储层 │
│ (数据仓库、数据集市、数据湖) │
├─────────────────────────────────────────────────┤
│ ETL处理层 │
│ (抽取Extract、转换Transform、加载Load) │
├─────────────────────────────────────────────────┤
│ 数据源层 │
│ (关系数据库、日志、文件、API、爬虫数据) │
└─────────────────────────────────────────────────┘
1.3 数据仓库分层
| 层次 | 英文名 | 说明 |
|---|---|---|
| ODS | Operational Data Store | 原始数据层,存放原始数据,保持数据原貌 |
| DWD | Data Warehouse Detail | 明细数据层,对ODS层数据进行清洗、规范化 |
| DWS | Data Warehouse Summary | 汇总数据层,按主题进行轻度汇总 |
| ADS | Application Data Store | 应用数据层,面向具体应用的结果数据 |
1.4 数据仓库模型
星型模型(Star Schema):
维度表1
\
维度表2 --- 事实表 --- 维度表3
/
维度表4
雪花模型(Snowflake Schema):
维度表 → 维度子表 → 事实表 ← 维度子表 ← 维度表
二、Hive简介
2.1 Hive与传统关系数据库的区别
| 比较项 | Hive | 传统关系数据库(RDBMS) |
|---|---|---|
| 数据规模 | PB级 | GB~TB级 |
| 查询语言 | HQL (类SQL) | SQL |
| 数据存储 | HDFS/S3等分布式存储 | 本地文件系统 |
| 执行引擎 | MapReduce/Tez/Spark | 自身引擎 |
| 数据写入 | 读多写少,批量加载 | 支持实时读写 |
| 延迟 | 高延迟(分钟~小时级) | 低延迟(毫秒~秒级) |
| 数据更新 | 不支持行级更新 | 支持行级更新和事务 |
| 索引 | 简单索引 | 复杂索引(B+树等) |
| Schema | 读时模式(Schema on Read) | 写时模式(Schema on Write) |
2.2 Hive系统架构
┌──────────────────────────────────────────┐
│ 用户接口层 │
│ (CLI命令行/JDBC/ODBC/Web UI) │
├──────────────────────────────────────────┤
│ 驱动层(Driver) │
│ ┌─────────┬──────────┬──────────┐ │
│ │ 解析器 │ 编译器 │ 优化器 │ │
│ │(Parser) │(Compiler) │(Optimizer)│ │
│ └─────────┴──────────┴──────────┘ │
├──────────────────────────────────────────┤
│ 元数据存储(Metastore) │
│ (MySQL/PostgreSQL/Derby) │
├──────────────────────────────────────────┤
│ 执行引擎 │
│ (MapReduce / Tez / Spark) │
├──────────────────────────────────────────┤
│ 分布式存储 │
│ (HDFS / S3) │
└──────────────────────────────────────────┘
2.3 Hive工作原理
用户输入HQL → 解析器(Parser)生成AST
→ 语义分析生成查询块 → 编译器(Compiler)生成逻辑执行计划
→ 优化器(Optimizer)优化计划 → 生成物理执行计划(MapReduce/Tez任务)
→ 执行引擎执行 → 返回结果
2.4 Hive数据存储模型
Database(数据库)
└── Table(表)
├── 内部表(Managed Table)
├── 外部表(External Table)
├── 分区表(Partitioned Table)
└── 分桶表(Bucketed Table)
存储路径: 数据文件存储在HDFS中,如:/user/hive/warehouse/数据库名.db/表名/
元数据: 存储在外部数据库(如MySQL)中,包括表结构、列信息、分区信息等。
三、Hive的部署
3.1 内嵌模式(Embedded Mode)
元数据存储在Hive自带的Derby数据库中,只允许一个用户连接,适用于测试。
配置文件 hive-site.xml:
<!-- 内嵌模式配置 -->
<configuration>
<!-- 指定元数据存储使用Derby数据库 -->
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:derby:;databaseName=metastore_db;create=true</value>
</property>
<!-- 指定JDBC驱动为Derby -->
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>org.apache.derby.jdbc.EmbeddedDriver</value>
</property>
</configuration>
启动方式:
# 直接启动Hive CLI,自动初始化Derby数据库
hive
3.2 本地模式(Local Mode)
元数据存储在本地MySQL数据库中,支持多用户访问(同一台机器上)。
配置文件 hive-site.xml:
<configuration>
<!-- 指定MySQL作为元数据存储数据库 -->
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://localhost:3306/hive_metastore?createDatabaseIfNotExist=true&useSSL=false</value>
</property>
<!-- 指定MySQL JDBC驱动类名 -->
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.cj.jdbc.Driver</value>
</property>
<!-- MySQL用户名 -->
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>root</value>
</property>
<!-- MySQL密码 -->
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>123456</value>
</property>
<!-- Hive元数据存储验证方式设为none -->
<property>
<name>hive.metastore.event.db.notification.api.auth</name>
<value>false</value>
</property>
</configuration>
初始化元数据库:
# 初始化MySQL中的Hive元数据库(只需执行一次)
schematool -dbType mysql -initSchema
3.3 远程模式(Remote Mode)
Metastore服务独立部署在一台机器上,其他机器通过Thrift协议远程访问。
服务端 hive-site.xml(Metastore Server所在机器):
<configuration>
<!-- MySQL连接配置(同本地模式) -->
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://dbhost:3306/hive_metastore?createDatabaseIfNotExist=true</value>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.cj.jdbc.Driver</value>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>root</value>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>123456</value>
</property>
<!-- 指定Metastore服务绑定的URI -->
<property>
<name>hive.metastore.uris</name>
<value>thrift://metastore-host:9083</value>
</property>
</configuration>
启动Metastore服务:
# 后台启动Metastore服务
hive --service metastore &
客户端 hive-site.xml(其他Hive客户端机器):
<configuration>
<!-- 指定远程Metastore服务的URI -->
<property>
<name>hive.metastore.uris</name>
<value>thrift://metastore-host:9083</value>
</property>
</configuration>
四、Hive数据类型
4.1 基本数据类型
| 数据类型 | 说明 | 示例 |
|---|---|---|
| TINYINT | 1字节有符号整数 | -128 ~ 127 |
| SMALLINT | 2字节有符号整数 | -32768 ~ 32767 |
| INT | 4字节有符号整数 | 2147483647 |
| BIGINT | 8字节有符号整数 | 9223372036854775807 |
| FLOAT | 4字节单精度浮点数 | 3.14F |
| DOUBLE | 8字节双精度浮点数 | 3.14159 |
| DECIMAL | 任意精度的有符号小数 | DECIMAL(10,2) |
| BOOLEAN | 布尔类型 | TRUE / FALSE |
| STRING | 字符串类型(无长度限制) | ‘hello world’ |
| VARCHAR | 变长字符串(需指定最大长度) | VARCHAR(100) |
| CHAR | 定长字符串 | CHAR(10) |
| TIMESTAMP | 时间戳 | ‘2024-01-01 12:00:00’ |
| DATE | 日期 | ‘2024-01-01’ |
| BINARY | 二进制字节序列 | — |
4.2 复杂数据类型
| 数据类型 | 说明 | 示例 |
|---|---|---|
| ARRAY | 有序同类型元素集合 | ARRAY |
| MAP | 键值对集合,键类型相同,值类型相同 | MAP<string, int> |
| STRUCT | 结构体,包含多个命名字段 | STRUCT<name:string, age:int> |
| UNIONTYPE | 联合类型,可存储多种类型之一 | UNIONTYPE<int, string> |
4.3 数据类型案例代码
-- ============================================================
-- 案例1:创建包含各种基本数据类型的表
-- ============================================================
CREATE TABLE data_type_demo (
id INT COMMENT '用户ID,4字节整数', -- 整数类型
username STRING COMMENT '用户名,字符串类型', -- 字符串类型
age TINYINT COMMENT '年龄,1字节整数', -- 小整数类型
salary DECIMAL(10,2) COMMENT '薪资,精确到小数点后2位', -- 精确小数
height DOUBLE COMMENT '身高,双精度浮点数', -- 浮点类型
is_active BOOLEAN COMMENT '是否激活', -- 布尔类型
birth_date DATE COMMENT '出生日期', -- 日期类型
create_time TIMESTAMP COMMENT '创建时间', -- 时间戳类型
bio VARCHAR(500) COMMENT '个人简介,最长500字符', -- 变长字符串
gender CHAR(1) COMMENT '性别,M/F,固定1字符' -- 定长字符串
)
COMMENT '数据类型演示表' -- 表注释
ROW FORMAT DELIMITED -- 指定行格式为分隔
FIELDS TERMINATED BY '\t' -- 字段间以制表符分隔
LINES TERMINATED BY '\n' -- 行间以换行符分隔
STORED AS TEXTFILE; -- 以文本文件格式存储
-- ============================================================
-- 案例2:创建包含复杂数据类型的表(ARRAY、MAP、STRUCT)
-- ============================================================
CREATE TABLE complex_type_demo (
student_id INT COMMENT '学生ID', -- 学生编号
student_name STRING COMMENT '学生姓名', -- 姓名
-- ARRAY类型:存储学生的所有课程成绩,有序列表
scores ARRAY<DOUBLE> COMMENT '各科成绩列表',
-- MAP类型:存储科目的名称到成绩的映射
course_scores MAP<STRING, DOUBLE> COMMENT '课程名称->成绩映射',
-- STRUCT类型:存储地址信息,包含多个命名字段
address STRUCT<
city:STRING, -- 城市
street:STRING, -- 街道
zip_code:STRING -- 邮编
> COMMENT '家庭地址结构体'
)
COMMENT '复杂数据类型演示表'
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t' -- 字段以制表符分隔
COLLECTION ITEMS TERMINATED BY ',' -- 集合元素以逗号分隔
MAP KEYS TERMINATED BY ':' -- MAP的键值对以冒号分隔
LINES TERMINATED BY '\n' -- 行以换行符分隔
STORED AS TEXTFILE; -- 文本文件格式存储
-- ============================================================
-- 案例3:向复杂类型表中插入并查询数据
-- ============================================================
-- 准备测试数据文件 complex_data.txt(每行一条记录):
-- 1001 张三 90.5,85.0,92.0 数学:90.5,语文:85.0,英语:92.0 北京市,朝阳区建国路,100022
-- 1002 李四 78.0,88.5,95.0 数学:78.0,语文:88.5,英语:95.0 上海市,浦东新区世纪大道,200120
-- 加载数据到表中
LOAD DATA LOCAL INPATH '/home/user/complex_data.txt' -- 本地文件路径
OVERWRITE INTO TABLE complex_type_demo; -- 覆盖写入目标表
-- 查询ARRAY类型:使用下标访问元素(从0开始)
SELECT
student_name, -- 学生姓名
scores[0] AS math_score, -- 第1个元素(数学成绩)
scores[1] AS chinese_score, -- 第2个元素(语文成绩)
SIZE(scores) AS total_subjects -- 数组中元素个数(共多少科)
FROM complex_type_demo;
-- 查询MAP类型:使用键名访问值
SELECT
student_name, -- 学生姓名
course_scores['数学'] AS math, -- 获取键为'数学'的值
course_scores['语文'] AS chinese, -- 获取键为'语文'的值
MAP_KEYS(course_scores) AS all_courses, -- 获取所有键
MAP_VALUES(course_scores) AS all_scores -- 获取所有值
FROM complex_type_demo;
-- 查询STRUCT类型:使用 点号(.) 访问字段
SELECT
student_name, -- 学生姓名
address.city AS city, -- 访问STRUCT中的city字段
address.street AS street, -- 访问STRUCT中的street字段
address.zip_code AS zip -- 访问STRUCT中的zip_code字段
FROM complex_type_demo;
五、数据库操作
5.1 创建数据库
-- ============================================================
-- 案例1:创建数据库的基本语法
-- ============================================================
CREATE DATABASE IF NOT EXISTS company_db -- 如果不存在则创建数据库company_db
COMMENT '公司业务数据库' -- 数据库的注释/描述信息
LOCATION '/user/hive/warehouse/company_db.db' -- 指定数据库在HDFS上的存储路径
WITH DBPROPERTIES ( -- 设置数据库的自定义属性
'creator' = 'admin', -- 创建者
'create_date' = '2024-01-01', -- 创建日期
'purpose' = '存储公司所有业务数据' -- 用途说明
);
-- ============================================================
-- 案例2:创建简单的数据库(使用默认路径)
-- ============================================================
CREATE DATABASE IF NOT EXISTS school_db; -- 最简形式,使用默认存储路径
-- ============================================================
-- 案例3:查看所有数据库
-- ============================================================
SHOW DATABASES; -- 列出所有数据库
-- ============================================================
-- 案例4:使用模糊匹配查找数据库
-- ============================================================
SHOW DATABASES LIKE 'company*'; -- 列出以company开头的数据库(通配符匹配)
SHOW DATABASES LIKE '*_db'; -- 列出以_db结尾的数据库
5.2 查看数据库信息
-- ============================================================
-- 案例1:查看数据库的详细描述信息
-- ============================================================
DESCRIBE DATABASE company_db; -- 显示数据库名称、注释、存储位置
-- ============================================================
-- 案例2:查看数据库的扩展描述信息(含自定义属性)
-- ============================================================
DESCRIBE DATABASE EXTENDED company_db; -- 额外显示DBPROPERTIES中的自定义属性
-- ============================================================
-- 案例3:使用DESC也可以查看
-- ============================================================
DESC DATABASE company_db; -- 等同于DESCRIBE DATABASE
-- ============================================================
-- 案例4:查看当前正在使用的数据库
-- ============================================================
SET hive.cli.print.current.db = true; -- 在CLI提示符中显示当前数据库名
-- 此后命令行提示符会变为 hive (company_db)>
-- ============================================================
-- 案例5:切换/使用数据库
-- ============================================================
USE company_db; -- 切换到company_db数据库
USE default; -- 切换回默认数据库
5.3 修改数据库属性
-- ============================================================
-- 案例1:修改数据库的自定义属性(DBPROPERTIES)
-- ============================================================
ALTER DATABASE company_db -- 修改company_db数据库
SET DBPROPERTIES ( -- 更新自定义属性
'creator' = 'zhangsan', -- 修改创建者
'edit_date' = '2024-06-15', -- 新增修改日期
'purpose' = '公司核心业务数据仓库' -- 修改用途描述
);
-- ============================================================
-- 案例2:修改数据库的所有者(Hive 2.x+ 支持)
-- ============================================================
ALTER DATABASE company_db -- 修改company_db数据库
SET OWNER USER 'lisi'; -- 将数据库所有者修改为lisi
-- ============================================================
-- 案例3:验证修改结果
-- ============================================================
DESCRIBE DATABASE EXTENDED company_db; -- 查看修改后的数据库属性
5.4 删除数据库
-- ============================================================
-- 案例1:删除空数据库
-- ============================================================
DROP DATABASE IF EXISTS school_db; -- 如果数据库存在且为空则删除
-- IF EXISTS:如果不存在不报错
-- ============================================================
-- 案例2:强制删除数据库(包含其中的表一起删除)
-- ============================================================
DROP DATABASE IF EXISTS school_db CASCADE; -- CASCADE:级联删除,数据库中的所有表
-- 和数据一起被删除
-- 生产环境慎用!
-- ============================================================
-- 案例3:查看删除结果
-- ============================================================
SHOW DATABASES LIKE 'school*'; -- 确认数据库已被删除
六、表操作
6.1 创建表
-- ============================================================
-- 案例1:创建内部表(Managed Table)——员工表
-- ============================================================
CREATE TABLE IF NOT EXISTS employees ( -- 如果不存在则创建
emp_id INT COMMENT '员工编号', -- 整数类型,员工ID
emp_name STRING COMMENT '员工姓名', -- 字符串,姓名
emp_age INT COMMENT '员工年龄', -- 整数,年龄
emp_salary DECIMAL(10,2) COMMENT '月薪', -- 精确小数,薪资
hire_date DATE COMMENT '入职日期', -- 日期类型
dept_name STRING COMMENT '所属部门名称' -- 字符串,部门
)
COMMENT '员工信息表(内部表)' -- 表注释
ROW FORMAT DELIMITED -- 行格式:使用分隔符
FIELDS TERMINATED BY '\t' -- 字段之间以Tab键分隔
LINES TERMINATED BY '\n' -- 记录之间以换行符分隔
STORED AS TEXTFILE -- 存储格式:纯文本
LOCATION '/user/hive/warehouse/company_db.db/employees'; -- 数据文件在HDFS上的路径
-- ============================================================
-- 案例2:创建外部表(External Table)—— 日志表
-- ============================================================
-- 外部表删除时只删除元数据,不删除HDFS上的实际数据文件
CREATE EXTERNAL TABLE IF NOT EXISTS access_logs ( -- EXTERNAL关键字指定为外部表
log_id BIGINT COMMENT '日志ID', -- 大整数,日志编号
user_id INT COMMENT '用户ID', -- 整数,用户编号
url STRING COMMENT '访问URL', -- 字符串,请求地址
ip_address STRING COMMENT 'IP地址', -- 字符串,客户端IP
access_time TIMESTAMP COMMENT '访问时间', -- 时间戳
status_code INT COMMENT 'HTTP状态码', -- 整数,响应码
user_agent STRING COMMENT '浏览器标识' -- 字符串,User-Agent
)
COMMENT 'Web访问日志表(外部表)'
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '|' -- 字段以竖线分隔
LINES TERMINATED BY '\n'
STORED AS TEXTFILE
LOCATION '/data/logs/access/'; -- 指向已有的HDFS数据目录
-- ============================================================
-- 案例3:创建分区表(Partitioned Table)—— 订单表按日期分区
-- ============================================================
CREATE TABLE IF NOT EXISTS orders (
order_id BIGINT COMMENT '订单ID', -- 订单编号
user_id INT COMMENT '用户ID', -- 用户编号
product_id INT COMMENT '商品ID', -- 商品编号
product_name STRING COMMENT '商品名称', -- 商品名
quantity INT COMMENT '购买数量', -- 数量
total_price DECIMAL(10,2) COMMENT '订单总金额', -- 总价
order_status STRING COMMENT '订单状态' -- 状态(待付款/已完成等)
)
COMMENT '订单信息表(按日期分区)'
PARTITIONED BY ( -- 定义分区字段
order_year STRING COMMENT '订单年份分区', -- 一级分区:年份
order_month STRING COMMENT '订单月份分区' -- 二级分区:月份
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
LINES TERMINATED BY '\n'
STORED AS ORC; -- 使用ORC列式存储格式(生产推荐)
-- ============================================================
-- 案例4:创建分桶表(Bucketed Table)
-- ============================================================
CREATE TABLE IF NOT EXISTS user_actions (
action_id BIGINT COMMENT '行为ID',
user_id INT COMMENT '用户ID',
action_type STRING COMMENT '行为类型(click/view/buy)',
action_time TIMESTAMP COMMENT '行为时间',
product_id INT COMMENT '商品ID'
)
COMMENT '用户行为表(分桶表,按user_id分桶)'
CLUSTERED BY (user_id) INTO 32 BUCKETS -- 按user_id哈希分桶,分成32个桶
SORTED BY (action_time) INTO BUCKETS -- 桶内按action_time排序
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
STORED AS ORC;
-- ============================================================
-- 案例5:使用CTAS创建表(从查询结果创建新表)
-- ============================================================
CREATE TABLE high_salary_employees AS -- 将查询结果创建为新表
SELECT
emp_id, -- 员工ID
emp_name, -- 姓名
emp_salary -- 薪资
FROM employees -- 来源表
WHERE emp_salary > 10000; -- 筛选薪资大于10000的记录
-- ============================================================
-- 案例6:复制表结构(不复制数据)
-- ============================================================
CREATE TABLE IF NOT EXISTS employees_copy -- 创建表副本
LIKE employees; -- 仅复制employees的表结构
-- ============================================================
-- 案例7:创建带Serde的表(处理JSON格式数据)
-- ============================================================
CREATE TABLE IF NOT EXISTS json_events (
event_id STRING COMMENT '事件ID',
event_type STRING COMMENT '事件类型',
event_data STRING COMMENT '事件数据'
)
COMMENT 'JSON格式事件表'
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe' -- 使用JSON序列化/反序列化器
STORED AS TEXTFILE;
6.2 查看表
-- ============================================================
-- 案例1:列出当前数据库下的所有表
-- ============================================================
SHOW TABLES; -- 显示当前数据库中的所有表名
-- ============================================================
-- 案例2:使用模糊匹配查找表
-- ============================================================
SHOW TABLES LIKE 'emp*'; -- 列出以emp开头的表
SHOW TABLES LIKE '*order*'; -- 列出包含order的表
-- ============================================================
-- 案例3:查看表的列信息
-- ============================================================
DESCRIBE employees; -- 显示表的列名、数据类型、注释
DESC employees; -- 简写形式,效果相同
-- ============================================================
-- 案例4:查看表的扩展信息(含分区、存储格式等详细信息)
-- ============================================================
DESCRIBE FORMATTED employees; -- 显示格式化的详细表信息
-- 包括:表类型、存储格式、分区信息、
-- 输入/输出格式、数据位置等
-- ============================================================
-- 案例5:查看表的列扩展信息
-- ============================================================
DESCRIBE EXTENDED employees; -- 以非格式化方式显示完整的表元数据
-- ============================================================
-- 案例6:查看表的创建语句
-- ============================================================
SHOW CREATE TABLE employees; -- 输出创建该表的完整DDL语句
-- 可用于复制表结构
-- ============================================================
-- 案例7:查看分区信息
-- ============================================================
SHOW PARTITIONS orders; -- 列出orders表的所有分区
6.3 修改表
-- ============================================================
-- 案例1:重命名表
-- ============================================================
ALTER TABLE employees -- 修改employees表
RENAME TO staff; -- 将表名改为staff
-- 再改回来
ALTER TABLE staff RENAME TO employees; -- 恢复原名
-- ============================================================
-- 案例2:添加新列
-- ============================================================
ALTER TABLE employees -- 修改employees表
ADD COLUMNS ( -- 添加新列
email STRING COMMENT '电子邮箱', -- 新增email列
phone STRING COMMENT '手机号码' -- 新增phone列
);
-- ============================================================
-- 案例3:替换所有列(用新列定义替换现有所有列)
-- ============================================================
ALTER TABLE employees -- 修改employees表
REPLACE COLUMNS ( -- 替换所有列定义(谨慎使用!)
emp_id INT COMMENT '员工编号',
emp_name STRING COMMENT '员工姓名',
emp_age INT COMMENT '年龄',
emp_salary DOUBLE COMMENT '薪资',
hire_date DATE COMMENT '入职日期',
dept_name STRING COMMENT '部门名称',
email STRING COMMENT '邮箱',
phone STRING COMMENT '电话'
);
-- ============================================================
-- 案例4:修改列名和数据类型
-- ============================================================
ALTER TABLE employees -- 修改employees表
CHANGE COLUMN emp_name -- 要修改的列名
employee_name STRING -- 新列名 新数据类型
COMMENT '员工全名'; -- 新注释
-- ============================================================
-- 案例5:修改表属性
-- ============================================================
ALTER TABLE employees -- 修改employees表
SET TBLPROPERTIES ( -- 设置表的自定义属性
'comment' = '公司全体员工信息表', -- 修改表注释
'owner' = 'hr_department', -- 设置所有者
'skip.header.line.count' = '1' -- 跳过CSV文件的第一行(标题行)
);
-- ============================================================
-- 案例6:为内部表设置Serde属性
-- ============================================================
ALTER TABLE employees -- 修改employees表
SET SERDE 'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe' -- 设置序列化类
WITH SERDEPROPERTIES ( -- 设置Serde属性
'field.delim' = ',', -- 字段分隔符改为逗号
'serialization.format' = ',' -- 序列化格式
);
6.4 删除表
-- ============================================================
-- 案例1:删除内部表
-- ============================================================
DROP TABLE IF EXISTS employees_copy; -- 删除内部表
-- 同时删除元数据和HDFS上的数据文件
-- IF EXISTS:如果表不存在也不会报错
-- ============================================================
-- 案例2:删除外部表
-- ============================================================
DROP TABLE IF EXISTS access_logs; -- 删除外部表
-- 仅删除元数据
-- HDFS上的数据文件保留不变
-- ============================================================
-- 案例3:清空表中的所有数据(保留表结构)
-- ============================================================
TRUNCATE TABLE high_salary_employees; -- 清空表数据,表结构保留
-- 仅适用于内部表
-- 不能用于外部表
6.5 修改分区
-- ============================================================
-- 案例1:添加分区
-- ============================================================
ALTER TABLE orders -- 修改orders表
ADD PARTITION ( -- 添加新分区
order_year = '2024', -- 一级分区:年份为2024
order_month = '01' -- 二级分区:月份为01
)
LOCATION '/user/hive/warehouse/company_db.db/orders/order_year=2024/order_month=01';
-- 指定该分区数据在HDFS上的存储路径
-- ============================================================
-- 案例2:添加多个分区
-- ============================================================
ALTER TABLE orders
ADD PARTITION (order_year = '2024', order_month = '02') -- 添加2024年2月分区
PARTITION (order_year = '2024', order_month = '03'); -- 添加2024年3月分区
-- ============================================================
-- 案例3:修改分区的存储位置
-- ============================================================
ALTER TABLE orders -- 修改orders表
PARTITION ( -- 指定要修改的分区
order_year = '2024',
order_month = '01'
)
SET LOCATION '/new/path/orders/2024/01'; -- 将该分区的存储路径改为新路径
-- ============================================================
-- 案例4:删除分区
-- ============================================================
ALTER TABLE orders -- 修改orders表
DROP IF EXISTS PARTITION ( -- 如果分区存在则删除
order_year = '2024',
order_month = '01'
);
-- ============================================================
-- 案例5:修复分区(当HDFS上有数据文件但Metastore中没有对应分区时使用)
-- ============================================================
MSCK REPAIR TABLE orders; -- 扫描HDFS目录,自动发现并注册缺失的分区
-- 常用于手动往HDFS目录上传数据后使用
-- ============================================================
-- 案例6:查看所有分区
-- ============================================================
SHOW PARTITIONS orders; -- 列出orders表的所有分区
-- 查看特定分区
SHOW PARTITIONS orders PARTITION (order_year = '2024'); -- 只列出2024年的分区
七、数据操作
7.1 导入数据
-- ============================================================
-- 案例1:从本地文件系统加载数据到Hive表(覆盖模式)
-- ============================================================
LOAD DATA LOCAL INPATH -- LOCAL关键字:数据源在本地文件系统
'/home/user/data/employees.txt' -- 本地文件的绝对路径
OVERWRITE INTO TABLE employees; -- OVERWRITE:覆盖表中已有数据
-- INTO:追加模式(去掉OVERWRITE即为追加)
-- ============================================================
-- 案例2:从本地文件系统加载数据到Hive表(追加模式)
-- ============================================================
LOAD DATA LOCAL INPATH -- 从本地加载
'/home/user/data/employees_new.txt' -- 本地文件路径
INTO TABLE employees; -- INTO(无OVERWRITE):追加到表中
-- ============================================================
-- 案例3:从HDFS加载数据到Hive表
-- ============================================================
-- 注意:从HDFS加载数据时,文件会被移动(非复制)到Hive表的目录中
LOAD DATA INPATH -- 无LOCAL关键字:数据源在HDFS上
'/data/input/employees.csv' -- HDFS上的文件路径
OVERWRITE INTO TABLE employees; -- 覆盖写入
-- ============================================================
-- 案例4:使用INSERT INTO追加数据
-- ============================================================
INSERT INTO TABLE employees -- INSERT INTO:追加数据到表中
VALUES
(1001, '张三', 28, 15000.00, '2023-03-15', '技术部'), -- 第1条记录
(1002, '李四', 32, 20000.00, '2022-07-01', '市场部'), -- 第2条记录
(1003, '王五', 25, 12000.00, '2024-01-10', '技术部'); -- 第3条记录
-- ============================================================
-- 案例5:使用INSERT OVERWRITE覆盖数据
-- ============================================================
INSERT OVERWRITE TABLE employees -- INSERT OVERWRITE:覆盖表中全部数据
SELECT
emp_id, -- 员工ID
emp_name, -- 姓名
emp_age, -- 年龄
emp_salary * 1.1 AS emp_salary, -- 薪资上调10%
hire_date, -- 入职日期
dept_name -- 部门名称
FROM temp_employees; -- 从临时表查询数据
-- ============================================================
-- 案例6:使用INSERT INTO通过SELECT语句追加数据
-- ============================================================
INSERT INTO TABLE employees -- 追加数据到employees表
SELECT
emp_id, -- 员工编号
emp_name, -- 姓名
emp_age, -- 年龄
emp_salary, -- 薪资
hire_date, -- 入职日期
dept_name -- 部门
FROM staging_employees -- 从暂存表读取
WHERE dept_name = '技术部'; -- 只导入技术部员工
-- ============================================================
-- 案例7:使用INSERT ... VALUES创建少量测试数据
-- ============================================================
INSERT INTO TABLE employees VALUES
(2001, '赵六', 30, 18000.50, '2023-06-15', '人事部'),
(2002, '钱七', 27, 13500.00, '2024-02-20', '财务部');
7.2 向分区导入数据
-- ============================================================
-- 案例1:静态分区导入——指定具体的分区值
-- ============================================================
INSERT INTO TABLE orders -- 追加数据到orders表
PARTITION ( -- 指定静态分区值
order_year = '2024', -- 分区字段:年份=2024
order_month = '06' -- 分区字段:月份=06
)
SELECT
order_id, -- 订单ID(注意不包含分区列)
user_id, -- 用户ID
product_id, -- 商品ID
product_name, -- 商品名称
quantity, -- 数量
total_price, -- 总价
order_status -- 订单状态
FROM raw_orders -- 来源表
WHERE order_date >= '2024-06-01' -- 筛选2024年6月的订单
AND order_date < '2024-07-01'; -- 且小于2024年7月
-- ============================================================
-- 案例2:动态分区导入——根据查询结果自动确定分区值
-- ============================================================
-- 开启动态分区功能
SET hive.exec.dynamic.partition = true; -- 开启动态分区(默认true)
SET hive.exec.dynamic.partition.mode = nonstrict; -- 设为非严格模式(允许全部动态分区)
SET hive.exec.max.dynamic.partitions = 1000; -- 最大动态分区数
SET hive.exec.max.dynamic.partitions.pernode = 100; -- 每个节点最大分区数
INSERT INTO TABLE orders -- 追加到orders表
PARTITION ( -- 分区字段
order_year, -- 动态分区:年份(从数据中获取值)
order_month -- 动态分区:月份(从数据中获取值)
)
SELECT
order_id, -- 订单ID
user_id, -- 用户ID
product_id, -- 商品ID
product_name, -- 商品名称
quantity, -- 数量
total_price, -- 总价
order_status, -- 订单状态
YEAR(order_date) AS order_year, -- 从order_date提取年份作为分区值
LPAD(MONTH(order_date), 2, '0') AS order_month -- 提取月份,左补零至2位
FROM raw_orders; -- 从原始订单表读取全部数据
-- ============================================================
-- 案例3:混合使用静态分区和动态分区
-- ============================================================
INSERT OVERWRITE TABLE orders -- 覆盖写入orders表
PARTITION ( -- 分区定义
order_year = '2024', -- 静态分区:年份固定为2024
order_month -- 动态分区:月份从数据中获取
)
SELECT
order_id, -- 订单ID
user_id, -- 用户ID
product_id, -- 商品ID
product_name, -- 商品名称
quantity, -- 数量
total_price, -- 总价
order_status, -- 订单状态
LPAD(MONTH(order_date), 2, '0') AS order_month -- 提取月份作为动态分区值
FROM raw_orders -- 来源表
WHERE YEAR(order_date) = 2024; -- 只处理2024年的数据
-- ============================================================
-- 案例4:使用LOAD DATA加载数据到指定分区
-- ============================================================
LOAD DATA LOCAL INPATH -- 从本地文件加载
'/home/user/data/orders_2024_jan.txt' -- 本地文件路径
INTO TABLE orders -- 目标表
PARTITION ( -- 加载到指定分区
order_year = '2024',
order_month = '01'
);
-- ============================================================
-- 案例5:从HDFS加载数据到指定分区
-- ============================================================
LOAD DATA INPATH -- 从HDFS加载
'/data/orders/2024_02.txt' -- HDFS文件路径
INTO TABLE orders -- 目标表
PARTITION ( -- 指定分区
order_year = '2024',
order_month = '02'
);
7.3 查询数据
-- ============================================================
-- 案例1:基本查询——选择所有列
-- ============================================================
SELECT * FROM employees; -- 查询employees表中的所有数据
-- 生产环境慎用,数据量大时会全表扫描
-- ============================================================
-- 案例2:选择特定列
-- ============================================================
SELECT
emp_id, -- 员工编号
emp_name, -- 员工姓名
emp_salary -- 薪资
FROM employees; -- 来源表
-- ============================================================
-- 案例3:使用WHERE子句过滤数据
-- ============================================================
SELECT
emp_id, -- 员工编号
emp_name, -- 员工姓名
emp_salary, -- 薪资
dept_name -- 部门
FROM employees -- 来源表
WHERE emp_salary > 15000 -- 条件1:薪资大于15000
AND dept_name = '技术部'; -- 条件2:部门为技术部
-- ============================================================
-- 案例4:使用LIKE进行模糊查询
-- ============================================================
SELECT
emp_id, -- 员工编号
emp_name -- 员工姓名
FROM employees
WHERE emp_name LIKE '张%'; -- 姓名以"张"开头,%匹配任意多个字符
-- 其他LIKE示例:
-- WHERE emp_name LIKE '_三' -- 姓名是两个字,第二个字是"三"(_匹配单个字符)
-- WHERE emp_name LIKE '%李%' -- 姓名中包含"李"字
-- ============================================================
-- 案例5:使用IN和BETWEEN
-- ============================================================
SELECT emp_id, emp_name, dept_name
FROM employees
WHERE dept_name IN ('技术部', '市场部', '人事部'); -- 部门在指定列表中
SELECT emp_id, emp_name, emp_salary
FROM employees
WHERE emp_salary BETWEEN 10000 AND 20000; -- 薪资在10000到20000之间(含两端)
-- ============================================================
-- 案例6:使用GROUP BY和聚合函数
-- ============================================================
SELECT
dept_name, -- 按部门分组
COUNT(*) AS emp_count, -- 统计每个部门的员工人数
AVG(emp_salary) AS avg_salary, -- 计算平均薪资
MAX(emp_salary) AS max_salary, -- 最高薪资
MIN(emp_salary) AS min_salary, -- 最低薪资
SUM(emp_salary) AS total_salary -- 薪资总额
FROM employees
GROUP BY dept_name -- 按部门名称分组
HAVING COUNT(*) >= 5 -- 只显示员工数>=5的部门(对分组结果过滤)
ORDER BY avg_salary DESC; -- 按平均薪资降序排列
-- ============================================================
-- 案例7:使用ORDER BY排序(全局排序,只使用一个Reducer)
-- ============================================================
SELECT emp_id, emp_name, emp_salary, dept_name
FROM employees
ORDER BY emp_salary DESC -- 按薪资降序排列
LIMIT 10; -- 只取前10条
-- ============================================================
-- 案例8:使用SORT BY排序(每个Reducer内部排序,非全局)
-- ============================================================
SET mapreduce.job.reduces = 3; -- 设置Reducer数量为3
SELECT emp_id, emp_name, emp_salary
FROM employees
SORT BY emp_salary DESC; -- 每个Reducer内部按薪资降序
-- ============================================================
-- 案例9:使用DISTRIBUTE BY和SORT BY组合
-- ============================================================
-- DISTRIBUTE BY:将数据按指定字段分发到不同Reducer
-- SORT BY:每个Reducer内部排序
SELECT emp_id, emp_name, emp_salary, dept_name
FROM employees
DISTRIBUTE BY dept_name -- 按部门分发到不同Reducer
SORT BY emp_salary DESC; -- 每个Reducer内部按薪资降序排序
-- ============================================================
-- 案例10:使用CLUSTER BY(等价于 DISTRIBUTE BY + SORT BY 同一字段升序)
-- ============================================================
SELECT emp_id, emp_name, emp_salary, dept_name
FROM employees
CLUSTER BY dept_name; -- 等价于 DISTRIBUTE BY dept_name SORT BY dept_name ASC
-- ============================================================
-- 案例11:使用LIMIT限制返回行数
-- ============================================================
SELECT * FROM employees LIMIT 5; -- 只返回前5条记录
SELECT * FROM employees LIMIT 5, 10; -- 跳过前5条,返回第6~15条(Hive 2.x+)
-- ============================================================
-- 案例12:使用CASE WHEN条件表达式
-- ============================================================
SELECT
emp_id, -- 员工编号
emp_name, -- 姓名
emp_salary, -- 薪资
CASE -- 条件判断
WHEN emp_salary >= 20000 THEN '高薪' -- 薪资>=20000,标记为'高薪'
WHEN emp_salary >= 15000 THEN '中等' -- 薪资>=15000且<20000,标记为'中等'
WHEN emp_salary >= 10000 THEN '普通' -- 薪资>=10000且<15000,标记为'普通'
ELSE '低薪' -- 其余情况,标记为'低薪'
END AS salary_level -- 新列名为salary_level
FROM employees;
-- ============================================================
-- 案例13:使用窗口函数
-- ============================================================
SELECT
emp_id, -- 员工编号
emp_name, -- 姓名
dept_name, -- 部门
emp_salary, -- 薪资
ROW_NUMBER() OVER ( -- 窗口函数:行号
PARTITION BY dept_name -- 按部门分组
ORDER BY emp_salary DESC -- 组内按薪资降序
) AS rank_in_dept, -- 在本部门的薪资排名
SUM(emp_salary) OVER ( -- 窗口函数:累计求和
PARTITION BY dept_name -- 按部门分组
) AS dept_total_salary -- 本部门薪资总额
FROM employees;
-- ============================================================
-- 案例14:使用子查询
-- ============================================================
-- 查找薪资高于公司平均薪资的员工
SELECT
emp_id, -- 员工编号
emp_name, -- 姓名
emp_salary -- 薪资
FROM employees
WHERE emp_salary > ( -- 子查询:计算公司平均薪资
SELECT AVG(emp_salary) FROM employees -- 内层查询返回平均薪资
);
-- ============================================================
-- 案例15:使用NULL相关函数
-- ============================================================
SELECT
emp_id, -- 员工编号
emp_name, -- 姓名
COALESCE(email, '未填写') AS email, -- 如果email为NULL则显示'未填写'
NVL(phone, '无') AS phone, -- NVL:如果phone为NULL则显示'无'
IF(email IS NULL, '缺失', '完整') AS info_status -- 判断是否为空
FROM employees;
7.4 查询插入
-- ============================================================
-- 案例1:将查询结果插入到另一张表(追加模式)
-- ============================================================
INSERT INTO TABLE tech_employees -- 追加插入到tech_employees表
SELECT
emp_id, -- 员工编号
emp_name, -- 姓名
emp_age, -- 年龄
emp_salary -- 薪资
FROM employees -- 来源表
WHERE dept_name = '技术部'; -- 只选取技术部员工
-- ============================================================
-- 案例2:将查询结果覆盖写入到另一张表
-- ============================================================
INSERT OVERWRITE TABLE tech_employees -- OVERWRITE:覆盖目标表中的现有数据
SELECT
emp_id, -- 员工编号
emp_name, -- 姓名
emp_age, -- 年龄
emp_salary * 1.05 AS emp_salary -- 薪资上浮5%
FROM employees
WHERE dept_name = '技术部';
-- ============================================================
-- 案例3:多表插入——一次查询插入多张表
-- ============================================================
-- Hive支持一次FROM多表插入(Multi-Table Insert)
FROM employees -- 数据来源表
INSERT OVERWRITE TABLE tech_report -- 插入到技术部报表表
SELECT
emp_id, emp_name, emp_salary -- 选择特定列
WHERE dept_name = '技术部' -- 过滤条件:技术部
AND emp_salary > 15000 -- 且薪资>15000
INSERT OVERWRITE TABLE market_report -- 插入到市场部报表表
SELECT
emp_id, emp_name, emp_salary -- 选择特定列
WHERE dept_name = '市场部' -- 过滤条件:市场部
AND emp_salary > 15000; -- 且薪资>15000
-- ============================================================
-- 案例4:将查询结果写入到HDFS目录(导出数据)
-- ============================================================
INSERT OVERWRITE DIRECTORY '/output/emp_report' -- 输出到HDFS目录
ROW FORMAT DELIMITED -- 指定输出格式
FIELDS TERMINATED BY ',' -- 字段以逗号分隔
LINES TERMINATED BY '\n' -- 行以换行符分隔
SELECT
emp_id, -- 员工编号
emp_name, -- 姓名
emp_salary, -- 薪资
dept_name -- 部门
FROM employees
WHERE dept_name = '技术部'; -- 只导出技术部数据
-- ============================================================
-- 案例5:将查询结果导出到本地文件系统
-- ============================================================
INSERT OVERWRITE LOCAL DIRECTORY '/home/user/output/emp_data' -- 输出到本地目录
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t' -- 字段以Tab分隔
SELECT
emp_id, -- 员工编号
emp_name, -- 姓名
emp_salary -- 薪资
FROM employees;
-- ============================================================
-- 案例6:查询结果插入到分区表
-- ============================================================
INSERT INTO TABLE orders -- 追加到orders表
PARTITION (order_year = '2024', order_month = '06') -- 指定静态分区
SELECT
order_id, -- 订单ID
user_id, -- 用户ID
product_id, -- 商品ID
product_name, -- 商品名称
quantity, -- 数量
total_price, -- 总价
order_status -- 订单状态
FROM staging_orders -- 来源暂存表
WHERE order_date LIKE '2024-06%'; -- 筛选2024年6月的数据
-- ============================================================
-- 案例7:使用CTAS一步完成建表+插入
-- ============================================================
CREATE TABLE dept_salary_summary AS -- 创建新表并填充数据
SELECT
dept_name, -- 部门名称
COUNT(*) AS total_employees, -- 员工总数
AVG(emp_salary) AS average_salary, -- 平均薪资
SUM(emp_salary) AS total_salary -- 薪资总额
FROM employees
GROUP BY dept_name; -- 按部门分组统计
7.5 关联查询
-- ============================================================
-- 准备:创建关联查询需要的表和数据
-- ============================================================
-- 员工表(已有)
-- 部门表
CREATE TABLE departments (
dept_id INT COMMENT '部门ID', -- 部门编号
dept_name STRING COMMENT '部门名称', -- 部门名称
dept_loc STRING COMMENT '部门所在地', -- 部门位置
manager_id INT COMMENT '部门经理ID' -- 经理的员工ID
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE;
-- 项目表
CREATE TABLE projects (
project_id INT COMMENT '项目ID',
project_name STRING COMMENT '项目名称',
dept_id INT COMMENT '所属部门ID',
budget DECIMAL(12,2) COMMENT '项目预算'
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE;
-- ============================================================
-- 案例1:内连接(INNER JOIN)——只返回两表中都匹配的记录
-- ============================================================
SELECT
e.emp_id, -- 员工编号
e.emp_name, -- 员工姓名
e.emp_salary, -- 薪资
d.dept_name, -- 部门名称
d.dept_loc -- 部门所在地
FROM employees e -- 主表(别名e)
INNER JOIN departments d -- 内连接departments表(别名d)
ON e.dept_name = d.dept_name; -- 连接条件:部门名称相同
-- ============================================================
-- 案例2:左外连接(LEFT OUTER JOIN)——返回左表所有记录,右表无匹配则填NULL
-- ============================================================
SELECT
e.emp_id, -- 员工编号
e.emp_name, -- 员工姓名
e.dept_name, -- 员工表中的部门名
d.dept_name AS dept_table_name, -- 部门表中的部门名(可能为NULL)
d.dept_loc -- 部门所在地(无匹配时为NULL)
FROM employees e -- 左表:所有员工都会保留
LEFT OUTER JOIN departments d -- 左外连接
ON e.dept_name = d.dept_name; -- 连接条件
-- ============================================================
-- 案例3:右外连接(RIGHT OUTER JOIN)——返回右表所有记录,左表无匹配则填NULL
-- ============================================================
SELECT
e.emp_id, -- 员工编号(无匹配时为NULL)
e.emp_name, -- 员工姓名(无匹配时为NULL)
d.dept_name, -- 部门名称
d.dept_loc -- 部门所在地
FROM employees e -- 左表
RIGHT OUTER JOIN departments d -- 右外连接:所有部门都会保留
ON e.dept_name = d.dept_name; -- 连接条件
-- ============================================================
-- 案例4:全外连接(FULL OUTER JOIN)——返回两表所有记录,无匹配的填NULL
-- ============================================================
SELECT
e.emp_id, -- 员工编号
e.emp_name, -- 员工姓名
COALESCE(e.dept_name, d.dept_name) AS dept, -- 合并两表的部门名(取非NULL值)
d.dept_loc -- 部门所在地
FROM employees e -- 左表
FULL OUTER JOIN departments d -- 全外连接:两表所有记录都保留
ON e.dept_name = d.dept_name; -- 连接条件
-- ============================================================
-- 案例5:左半连接(LEFT SEMI JOIN)——只返回左表中在右表有匹配的记录
-- 不返回右表的任何列,类似于 EXISTS 子查询
-- ============================================================
SELECT
e.emp_id, -- 员工编号
e.emp_name, -- 员工姓名
e.dept_name -- 部门
FROM employees e -- 左表
LEFT SEMI JOIN departments d -- 左半连接
ON e.dept_name = d.dept_name; -- 连接条件:只判断是否存在匹配
-- ============================================================
-- 案例6:交叉连接(CROSS JOIN / 笛卡尔积)——左表每行 × 右表每行
-- ============================================================
-- 警告:数据量大时会产生海量结果,生产环境慎用!
SELECT
e.emp_name, -- 员工姓名
d.dept_name -- 部门名称
FROM employees e -- 左表
CROSS JOIN departments d; -- 交叉连接:每个员工 × 每个部门
-- ============================================================
-- 案例7:多表关联查询(3表JOIN)
-- ============================================================
SELECT
e.emp_id, -- 员工编号
e.emp_name, -- 员工姓名
d.dept_name, -- 部门名称
d.dept_loc, -- 部门所在地
p.project_name, -- 项目名称
p.budget -- 项目预算
FROM employees e -- 员工表
INNER JOIN departments d -- 第1个连接:员工-部门
ON e.dept_name = d.dept_name -- 连接条件
INNER JOIN projects p -- 第2个连接:部门-项目
ON d.dept_id = p.dept_id; -- 连接条件
-- ============================================================
-- 案例8:自连接(Self Join)——表与自身关联
-- ============================================================
-- 查找同部门中薪资不同的员工对
SELECT
e1.emp_name AS employee_1, -- 员工1的姓名
e2.emp_name AS employee_2, -- 员工2的姓名
e1.dept_name AS department, -- 共同的部门
e1.emp_salary AS salary_1, -- 员工1的薪资
e2.emp_salary AS salary_2 -- 员工2的薪资
FROM employees e1 -- 第1个实例
INNER JOIN employees e2 -- 第2个实例(同一张表)
ON e1.dept_name = e2.dept_name -- 同一个部门
AND e1.emp_id < e2.emp_id; -- 避免重复配对和自配对
-- ============================================================
-- 案例9:使用JOIN的WHERE过滤(注意与ON条件的区别)
-- ============================================================
-- ON条件:在JOIN时过滤,影响连接结果
-- WHERE条件:在JOIN后过滤,对连接结果再筛选
SELECT
e.emp_name, -- 员工姓名
d.dept_name, -- 部门名称
d.dept_loc -- 部门位置
FROM employees e -- 员工表
LEFT OUTER JOIN departments d -- 左外连接
ON e.dept_name = d.dept_name -- ON:连接条件
WHERE d.dept_loc = '北京'; -- WHERE:过滤结果(排除了NULL行,
-- 此时效果等同于INNER JOIN)
-- ============================================================
-- 案例10:使用MAPJOIN提示(小表广播优化)
-- ============================================================
-- 当其中一个表很小时,可以使用MAPJOIN将其加载到内存中广播
SELECT /*+ MAPJOIN(d) */ -- MAPJOIN提示:将d表(小表)加载到内存
e.emp_id, -- 员工编号
e.emp_name, -- 员工姓名
d.dept_name, -- 部门名称
d.dept_loc -- 部门位置
FROM employees e -- 大表
INNER JOIN departments d -- 小表(会被广播到所有Map任务)
ON e.dept_name = d.dept_name; -- 连接条件
-- ============================================================
-- 案例11:使用UNION合并多个查询结果
-- ============================================================
-- UNION DISTINCT:合并并去重(默认)
SELECT emp_id, emp_name, '技术部' AS source
FROM employees
WHERE dept_name = '技术部'
UNION DISTINCT -- 合并两个查询结果并去重
SELECT emp_id, emp_name, '市场部' AS source
FROM employees
WHERE dept_name = '市场部'
ORDER BY emp_id; -- 最终结果按emp_id排序
-- ============================================================
-- 案例12:使用UNION ALL(合并不去重,性能更好)
-- ============================================================
SELECT emp_id, emp_name, emp_salary, '在职' AS status
FROM employees
WHERE is_active = true
UNION ALL -- 合并不去重(保留所有记录)
SELECT emp_id, emp_name, emp_salary, '离职' AS status
FROM former_employees
WHERE leave_date >= '2024-01-01'
ORDER BY emp_id;
八、本章小结
核心知识点总结
┌─────────────────────────────────────────────────────────┐
│ Hive知识体系 │
├──────────────┬──────────────────────────────────────────┤
│ 数据仓库概念 │ 面向主题、集成、稳定、时变 │
│ │ 分层:ODS → DWD → DWS → ADS │
│ │ 模型:星型模型、雪花模型 │
├──────────────┼──────────────────────────────────────────┤
│ Hive本质 │ 基于Hadoop的数据仓库工具 │
│ │ 将HQL转为MapReduce/Tez/Spark任务执行 │
│ │ 读时模式(Schema on Read) │
├──────────────┼──────────────────────────────────────────┤
│ 部署模式 │ 内嵌模式(Derby,单用户) │
│ │ 本地模式(MySQL,多用户) │
│ │ 远程模式(Thrift,分布式) │
├──────────────┼──────────────────────────────────────────┤
│ 数据类型 │ 基本:INT/STRING/DECIMAL/BOOLEAN/DATE... │
│ │ 复杂:ARRAY/MAP/STRUCT/UNIONTYPE │
├──────────────┼──────────────────────────────────────────┤
│ 数据库操作 │ CREATE/SHOW/DESCRIBE/ALTER/DROP DATABASE │
├──────────────┼──────────────────────────────────────────┤
│ 表操作 │ 内部表 vs 外部表(EXTERNAL) │
│ │ 分区表(PARTITIONED BY) │
│ │ 分桶表(CLUSTERED BY) │
│ │ CREATE/SHOW/DESCRIBE/ALTER/DROP TABLE │
├──────────────┼──────────────────────────────────────────┤
│ 数据操作 │ LOAD DATA (本地/HDFS) │
│ │ INSERT INTO/OVERWRITE + SELECT │
│ │ 静态分区 vs 动态分区 │
├──────────────┼──────────────────────────────────────────┤
│ 查询语法 │ SELECT...FROM...WHERE...GROUP BY... │
│ │ HAVING...ORDER BY...LIMIT │
│ │ JOIN: INNER/LEFT/RIGHT/FULL/SEMI/CROSS │
│ │ UNION/UNION ALL │
│ │ 子查询、窗口函数、CASE WHEN │
└──────────────┴──────────────────────────────────────────┘
常用配置参数速查
-- ============================================================
-- Hive常用配置参数(SET命令设置)
-- ============================================================
-- 显示当前数据库名
SET hive.cli.print.current.db = true; -- 在CLI提示符中显示当前数据库
-- 显示查询结果的列头
SET hive.cli.print.header = true; -- 查询结果第一行显示列名
-- 动态分区设置
SET hive.exec.dynamic.partition = true; -- 开启动态分区
SET hive.exec.dynamic.partition.mode = nonstrict; -- 允许全部字段都是动态分区
-- MapJoin优化(小表自动广播)
SET hive.auto.convert.join = true; -- 自动将小表进行MapJoin优化
SET hive.mapjoin.smalltable.filesize = 25000000; -- 小表阈值:25MB
-- 设置执行引擎
SET hive.execution.engine = mr; -- 使用MapReduce引擎
-- SET hive.execution.engine = tez; -- 使用Tez引擎(更快)
-- SET hive.execution.engine = spark; -- 使用Spark引擎
-- 并行执行
SET hive.exec.parallel = true; -- 允许并行执行独立的MapReduce阶段
SET hive.exec.parallel.thread.number = 8; -- 最大并行度
-- 输出压缩
SET hive.exec.compress.output = true; -- 开启输出压缩
SET mapreduce.output.fileoutputformat.compress.codec =
org.apache.hadoop.io.compress.SnappyCodec; -- 使用Snappy压缩
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐




所有评论(0)