实操练习手册

实操练习手册

一套贯穿全站的数据集 + 21 道可动手的练习题。所有“参考答案”都配了实际运行结果,照着敲一遍,本课程最核心的 HiveQL 能力就练到了。

4 张表103 条样例数据21 道练习每题带参考答案与结果
怎么用这一页① 先按“数据集准备”把四张表建好、数据加载进去(也可以直接用自己已有的表);② 打开 Beeline 或 DataGrip,从第 1 题开始逐题写;③ 写完对照“参考答案”,重点看写法和结果是否一致;④ 每道题的“参考结果”是用本页数据集实算出来的,结果对不上说明条件写错了。

一、数据集准备 数据取自课程《第11周 hive sql综合训练(一)》

这是一个“校园选课成绩”场景,共四张表:学生信息、课程信息、老师信息、成绩。

表名说明字段行数
student_info学生信息表stu_id, stu_name, birthday, sex20
course_info课程信息表course_id, course_name, tea_id5
teacher_info老师信息表tea_id, tea_name4
score_info成绩表stu_id, course_id, score74

1. 建表语句

学生信息表 · student_info

SQL
CREATE TABLE IF NOT EXISTS student_info(
    stu_id   STRING COMMENT '学生id',
    stu_name STRING COMMENT '学生姓名',
    birthday STRING COMMENT '出生日期',
    sex      STRING COMMENT '性别'
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE;

课程信息表 · course_info

SQL
CREATE TABLE IF NOT EXISTS course_info(
    course_id   STRING COMMENT '课程id',
    course_name STRING COMMENT '课程名',
    tea_id      STRING COMMENT '任课老师id'
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE;

老师信息表 · teacher_info

SQL
CREATE TABLE IF NOT EXISTS teacher_info(
    tea_id   STRING COMMENT '老师id',
    tea_name STRING COMMENT '老师姓名'
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE;

成绩表 · score_info

SQL
CREATE TABLE IF NOT EXISTS score_info(
    stu_id    STRING COMMENT '学生id',
    course_id STRING COMMENT '课程id',
    score     INT    COMMENT '成绩'
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE;

2. 加载数据

SQL
-- 把四个数据文件放在 /home/hadoop/shell/data 目录下,然后逐张表加载
LOAD DATA LOCAL INPATH '/home/hadoop/shell/data/student_info.txt'  INTO TABLE student_info;
LOAD DATA LOCAL INPATH '/home/hadoop/shell/data/course_info.txt'   INTO TABLE course_info;
LOAD DATA LOCAL INPATH '/home/hadoop/shell/data/teacher_info.txt'  INTO TABLE teacher_info;
LOAD DATA LOCAL INPATH '/home/hadoop/shell/data/score_info.txt'    INTO TABLE score_info;

3. 样例数据(原文数据文件内容)

📄 student_info(学生信息表)共 20 行
stu_idstu_namebirthdaysex
001彭于晏1995-05-16男
002胡歌1994-03-20男
003周杰伦1995-04-30男
004刘德华1998-08-28男
005唐国强1993-09-10男
006陈道明1992-11-12男
007陈坤1999-04-09男
008吴京1994-02-06男
009郭德纲1992-12-05男
010于谦1998-08-23男
011潘长江1995-05-27男
012杨紫1996-12-21女
013蒋欣1997-11-08女
014赵丽颖1990-01-09女
015刘亦菲1993-01-14女
016周冬雨1990-06-18女
017范冰冰1992-07-04女
018李冰冰1993-09-24女
019邓紫棋1994-08-31女
020宋丹丹1991-03-01女
📄 course_info(课程信息表)共 5 行
course_idcourse_nametea_id
01语文1003
02数学1001
03英语1004
04体育1002
05音乐1002
📄 teacher_info(老师信息表)共 4 行
tea_idtea_name
1001张高数
1002李体音
1003王子文
1004刘丽英
📄 score_info(成绩表)共 74 行
stu_idcourse_idscore
0010194
0020174
0040185
0050164
0060171
0070148
0080156
0090175
0100184
0110161
0120144
0130147
0140181
0150190
0160171
0170158
0180138
0190146
0200189
0010263
0020284
0040293
0050244
0060290
0070255
0080234
0090278
0100268
0110249
0120274

(仅显示前 30 行,完整数据见原始资料库中的第11周实训文档)

还没有集群?也能练本页“参考结果”是在同一份数据上用标准 SQL 语义复算出来的。你可以把建表语句换成普通关系型数据库的写法(去掉 ROW FORMAT / STORED AS 等 Hive 专有子句),在 MySQL、SQLite 里先把查询逻辑练熟,再回到 Hive 上跑一遍——SQL 部分是通用的,Hive 的差异主要在存储、分区和函数上。

二、练习题 按单元递进:DDL → DML → 查询 → 函数 → 优化

单元3 Hive数据定义

01

在 Hive 里建一个名为 school 的数据库,并切换到它。 基础

提示:CREATE DATABASE + USE

解题思路CREATE DATABASE 后面可以跟 COMMENT 和 LOCATION;建完用 USE 切进去,后面建表才会落到这个库里。
查看参考答案
SQL
CREATE DATABASE IF NOT EXISTS school COMMENT '校园选课库';
USE school;
02

建一张学生表 student_info(学号、姓名、生日、性别),用逗号分隔、文本格式存储。 基础

提示:ROW FORMAT DELIMITED FIELDS TERMINATED BY

解题思路建表三段式:字段清单 → 怎么分隔(ROW FORMAT…)→ 存成什么格式(STORED AS…)。练手一律先用 TEXTFILE。
查看参考答案
SQL
CREATE TABLE IF NOT EXISTS student_info(
    stu_id STRING, stu_name STRING, birthday STRING, sex STRING
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE;
03

把 /home/hadoop/data/student_info.txt 加载到学生表,并确认行数。 基础

提示:LOAD DATA LOCAL INPATH + count(*)

解题思路LOAD DATA 用 LOCAL 表示从客户端本机读文件;加载完立刻 count(*) 验证行数,这是必备习惯。
查看参考答案与结果
SQL
LOAD DATA LOCAL INPATH '/home/hadoop/data/student_info.txt' INTO TABLE student_info;
SELECT count(*) FROM student_info;
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异)
cnt
20
04

建一张按课程分区的成绩表 score_part,并把 score_info 的数据按 course_id 动态写入。 进阶

提示:PARTITIONED BY + 动态分区参数

解题思路分区列不要写在字段清单里(它是目录名)。写入用动态分区:SELECT 的最后几列要和 PARTITION 里写的列顺序一致。
查看参考答案与结果
SQL
CREATE TABLE IF NOT EXISTS score_part(
    stu_id STRING, score INT
)
PARTITIONED BY (course_id STRING)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ','
STORED AS TEXTFILE;

SET hive.exec.dynamic.partition = true;
SET hive.exec.dynamic.partition.mode = nonstrict;

INSERT OVERWRITE TABLE score_part PARTITION(course_id)
SELECT stu_id, score, course_id FROM score_info;

SHOW PARTITIONS score_part;
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异)
course_idcnt
0119
0219
0319
0412
055
05

建一张按学号分成 4 个桶的成绩表,并从中抽第 1 个桶的数据。 进阶

提示:CLUSTERED BY … INTO n BUCKETS + TABLESAMPLE

解题思路桶数写在建表语句里;分桶表只能用 INSERT … SELECT 写入,不能 LOAD。抽样是 TABLESAMPLE(BUCKET x OUT OF y ON 列)。
查看参考答案
SQL
CREATE TABLE IF NOT EXISTS score_bucket(
    stu_id STRING, course_id STRING, score INT
)
CLUSTERED BY (stu_id) INTO 4 BUCKETS
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' STORED AS TEXTFILE;

INSERT OVERWRITE TABLE score_bucket SELECT * FROM score_info;

SELECT * FROM score_bucket TABLESAMPLE(BUCKET 1 OUT OF 4 ON stu_id);
06

创建一个视图 v_score_detail,包含学生姓名、课程名、老师姓名、成绩四个字段。 进阶

提示:CREATE VIEW … AS SELECT

解题思路视图 = 把一段查询存起来。四张表按外键串起来:成绩→学生、成绩→课程、课程→老师。
查看参考答案与结果
SQL
CREATE VIEW IF NOT EXISTS v_score_detail AS
SELECT s.stu_name, c.course_name, t.tea_name, sc.score
FROM score_info sc
JOIN student_info s ON sc.stu_id = s.stu_id
JOIN course_info  c ON sc.course_id = c.course_id
JOIN teacher_info t ON c.tea_id = t.tea_id;

SELECT * FROM v_score_detail LIMIT 5;
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异)
stu_namecourse_nametea_namescore
彭于晏语文王子文94
胡歌语文王子文74
刘德华语文王子文85
唐国强语文王子文64
陈道明语文王子文71

单元4 Hive数据操作

07

用查询结果建一张“男生信息表” stu_male。 基础

提示:CREATE TABLE + INSERT … SELECT

解题思路两步走:先 CREATE TABLE 建好结构(字段要和 SELECT 出来的列一一对应),再 INSERT … SELECT 灌数据。
查看参考答案与结果
SQL
CREATE TABLE IF NOT EXISTS stu_male(
    stu_id STRING, stu_name STRING, birthday STRING, sex STRING
) ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' STORED AS TEXTFILE;

INSERT INTO stu_male SELECT * FROM student_info WHERE sex = '男';
SELECT count(*) FROM stu_male;
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异)
male_count
11

单元5 Hive数据查询

08

查出所有女生的学号、姓名和生日。 基础

提示:WHERE sex = '女'

解题思路一个 WHERE 就够了:WHERE sex = '女'。注意字符串用英文单引号。
查看参考答案与结果
SQL
SELECT stu_id, stu_name, birthday FROM student_info WHERE sex = '女';
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异)
stu_idstu_namebirthday
012杨紫1996-12-21
013蒋欣1997-11-08
014赵丽颖1990-01-09
015刘亦菲1993-01-14
016周冬雨1990-06-18
017范冰冰1992-07-04
018李冰冰1993-09-24
019邓紫棋1994-08-31
020宋丹丹1991-03-01
09

查出姓“刘”的学生姓名。 基础

提示:LIKE '刘%'

解题思路LIKE '刘%':% 代表"任意多个字符"。想匹配一个字用下划线 _。
查看参考答案与结果
SQL
SELECT stu_name FROM student_info WHERE stu_name LIKE '刘%';
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异)
stu_name
刘德华
刘亦菲
10

统计每门课程的选课人数和平均分(平均分保留两位小数),按课程号升序。 基础

提示:GROUP BY + count + round(avg(),2)

解题思路GROUP BY course_id 之后,人数用 count(*),平均分用 round(avg(score),2)。
查看参考答案与结果
SQL
SELECT course_id, count(*) AS 人数, round(avg(score), 2) AS 平均分
FROM score_info GROUP BY course_id ORDER BY course_id;
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异)
course_idcntavg_score
011967.16
021959.63
031969.42
041263.42
05574.6
11

找出平均分高于 65 分的课程,按平均分降序显示课程名与平均分。 进阶

提示:子查询/CTE + JOIN course_info

解题思路两步:① 先分组算平均分并用 HAVING avg(score) > 65 过滤;② 再和 course_info 关联取出课程名。用 WITH 写最清楚。
查看参考答案与结果
SQL
WITH avg_t AS (
    SELECT course_id, round(avg(score), 2) AS avg_score
    FROM score_info GROUP BY course_id HAVING avg(score) > 65
)
SELECT c.course_name, a.avg_score
FROM avg_t a JOIN course_info c ON a.course_id = c.course_id
ORDER BY a.avg_score DESC;
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异)
course_nameavg_score
音乐74.6
英语69.42
语文67.16
12

按总分降序找出前 5 名学生,显示姓名和总分。 进阶

提示:GROUP BY + sum + JOIN + ORDER BY DESC LIMIT

解题思路先 GROUP BY 学生求 sum(score),再 JOIN 学生表拿姓名,最后 ORDER BY 总分 DESC LIMIT 5。
查看参考答案与结果
SQL
SELECT s.stu_name, sum(sc.score) AS 总分
FROM score_info sc JOIN student_info s ON sc.stu_id = s.stu_id
GROUP BY s.stu_name
ORDER BY 总分 DESC
LIMIT 5;
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异)
stu_nametotal
唐国强377
郭德纲371
胡歌345
刘德华326
周冬雨325
13

查出每门课的任课老师是谁(课程名 + 老师名)。 进阶

提示:course_info JOIN teacher_info

解题思路两张表通过 tea_id 内连接即可,不需要成绩表。
查看参考答案与结果
SQL
SELECT c.course_name, t.tea_name
FROM course_info c JOIN teacher_info t ON c.tea_id = t.tea_id
ORDER BY c.course_id;
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异)
course_nametea_name
语文王子文
数学张高数
英语刘丽英
体育李体音
音乐李体音
14

找出所有没有参加 05 号课程考试的学生。 进阶

提示:LEFT JOIN + IS NULL

解题思路关键点:关联条件里的 sc.course_id = '05' 要写在 ON 里而不是 WHERE 里,再加 WHERE sc.stu_id IS NULL 取差集。
查看参考答案与结果
SQL
SELECT s.stu_id, s.stu_name
FROM student_info s
LEFT JOIN score_info sc ON s.stu_id = sc.stu_id AND sc.course_id = '05'
WHERE sc.stu_id IS NULL
ORDER BY s.stu_id;
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异),共 15 行,此处显示前 12 行
stu_idstu_name
001彭于晏
002胡歌
003周杰伦
004刘德华
006陈道明
008吴京
010于谦
011潘长江
012杨紫
013蒋欣
014赵丽颖
016周冬雨
15

统计男、女生的平均分各是多少。 进阶

提示:JOIN 后再 GROUP BY sex

解题思路先 JOIN 学生表拿到性别,再 GROUP BY s.sex。分组列写 s.sex 更稳妥。
查看参考答案与结果
SQL
SELECT s.sex, round(avg(sc.score), 2) AS 平均分, count(*) AS 记录数
FROM score_info sc JOIN student_info s ON sc.stu_id = s.stu_id
GROUP BY s.sex;
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异)
sexavg_scorecnt
女61.5334
男69.2540

单元6 Hive函数应用

16

用 CASE WHEN 把 01 号课程的成绩分成 优秀(≥90)/良好(≥80)/及格(≥60)/不及格 四档,并统计各档人数。 进阶

提示:CASE WHEN + 外层 GROUP BY

解题思路用 CASE WHEN 分档;Hive 不允许按 SELECT 里的别名分组,所以 GROUP BY 后面要把整段 CASE WHEN 再写一遍(或者外面再套一层子查询)。
查看参考答案与结果
SQL
SELECT CASE WHEN score >= 90 THEN '优秀'
             WHEN score >= 80 THEN '良好'
             WHEN score >= 60 THEN '及格'
             ELSE '不及格' END AS 等级,
       count(*) AS 人数
FROM score_info
WHERE course_id = '01'
GROUP BY CASE WHEN score >= 90 THEN '优秀'
              WHEN score >= 80 THEN '良好'
              WHEN score >= 60 THEN '及格'
              ELSE '不及格' END
ORDER BY 人数 DESC;
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异)
lvcnt
不及格7
及格6
良好4
优秀2
17

用窗口函数取每门课程成绩前 3 名的学生(显示课程、学号、成绩、名次)。 高阶

提示:rank() OVER (PARTITION BY … ORDER BY …)

解题思路窗口函数要写在子查询里(WHERE 不能直接用窗口函数的结果):rank() OVER (PARTITION BY course_id ORDER BY score DESC),外层再筛 rk <= 3。
查看参考答案与结果
SQL
SELECT * FROM (
    SELECT course_id, stu_id, score,
           rank() OVER (PARTITION BY course_id ORDER BY score DESC) AS rk
    FROM score_info
) t
WHERE rk <= 3
ORDER BY course_id, rk;
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异),共 15 行,此处显示前 12 行
course_idstu_idscorerk
01001941
01015902
01020893
02004931
02006902
02016893
03005991
03013932
03019932
040021001
04016942
04005853
18

把学生姓名转成大写,并拼上性别,输出形如 “彭于晏(男)” 的标签。 进阶

提示:upper + concat

解题思路两个函数套用:concat(upper(stu_name), '(', sex, ')')。
查看参考答案与结果
SQL
SELECT concat(upper(stu_name), '(', sex, ')') AS 标签
FROM student_info LIMIT 5;
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异)
label
彭于晏(男)
胡歌(男)
周杰伦(男)
刘德华(男)
唐国强(男)
19

统计每门课程成绩的标准差(保留 2 位小数),并按标准差降序。 高阶

提示:stddev() 或 stddev_samp()

解题思路标准差用聚合函数 stddev(score),和 avg 一样配合 GROUP BY 使用。
查看参考答案
SQL
SELECT course_id,
       count(*) AS 人数,
       round(stddev(score), 2) AS 标准差
FROM score_info
GROUP BY course_id
ORDER BY 标准差 DESC;

单元7 Hive性能优化

20

假设 course_id='01' 的数据量特别大导致数据倾斜,写出两阶段聚合的 SQL。 高阶

提示:加盐 → 局部聚合 → 去盐 → 全局聚合

解题思路两阶段聚合:① 子查询里 GROUP BY course_id, cast(rand()*10 AS INT) 先做局部计数(加盐打散);② 外层 GROUP BY course_id 把局部结果 sum 起来(去盐)。
查看参考答案与结果
SQL
WITH stage1 AS (
    SELECT course_id,
           cast(rand() * 10 AS INT) AS salt,
           count(*) AS part_cnt
    FROM score_info
    GROUP BY course_id, cast(rand() * 10 AS INT)
)
SELECT course_id, sum(part_cnt) AS cnt
FROM stage1
GROUP BY course_id
ORDER BY course_id;
预期结果(用课程数据集在本机 SQL 引擎复算;实际 Hive 输出的表头与格式会略有差异)
course_idcnt
0119
0219
0319
0412
055
21

建一张 ORC 格式、Snappy 压缩的成绩表,并说明它比 TEXTFILE 快在哪。 进阶

提示:STORED AS ORC + TBLPROPERTIES

解题思路建表时写 STORED AS ORC,再用 TBLPROPERTIES('orc.compress'='SNAPPY') 指定压缩。
查看参考答案与结果
SQL
CREATE TABLE IF NOT EXISTS score_orc(
    stu_id STRING, course_id STRING, score INT
)
STORED AS ORC
TBLPROPERTIES ('orc.compress' = 'SNAPPY');

INSERT INTO score_orc SELECT * FROM score_info;
ORC 是列存 + 自带索引 + 高压缩率:只读用到的列、能跳过不满足条件的数据块,所以扫描量小、速度快。

三、练完之后