实操练习手册
一套贯穿全站的数据集 + 21 道可动手的练习题。所有“参考答案”都配了实际运行结果,照着敲一遍,本课程最核心的 HiveQL 能力就练到了。
一、数据集准备 数据取自课程《第11周 hive sql综合训练(一)》
这是一个“校园选课成绩”场景,共四张表:学生信息、课程信息、老师信息、成绩。
| 表名 | 说明 | 字段 | 行数 |
|---|---|---|---|
| student_info | 学生信息表 | stu_id, stu_name, birthday, sex | 20 |
| course_info | 课程信息表 | course_id, course_name, tea_id | 5 |
| teacher_info | 老师信息表 | tea_id, tea_name | 4 |
| score_info | 成绩表 | stu_id, course_id, score | 74 |
1. 建表语句
学生信息表 · student_info
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
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
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
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. 加载数据
-- 把四个数据文件放在 /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_id | stu_name | birthday | sex |
|---|---|---|---|
| 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_id | course_name | tea_id |
|---|---|---|
| 01 | 语文 | 1003 |
| 02 | 数学 | 1001 |
| 03 | 英语 | 1004 |
| 04 | 体育 | 1002 |
| 05 | 音乐 | 1002 |
📄 teacher_info(老师信息表)共 4 行
| tea_id | tea_name |
|---|---|
| 1001 | 张高数 |
| 1002 | 李体音 |
| 1003 | 王子文 |
| 1004 | 刘丽英 |
📄 score_info(成绩表)共 74 行
| stu_id | course_id | score |
|---|---|---|
| 001 | 01 | 94 |
| 002 | 01 | 74 |
| 004 | 01 | 85 |
| 005 | 01 | 64 |
| 006 | 01 | 71 |
| 007 | 01 | 48 |
| 008 | 01 | 56 |
| 009 | 01 | 75 |
| 010 | 01 | 84 |
| 011 | 01 | 61 |
| 012 | 01 | 44 |
| 013 | 01 | 47 |
| 014 | 01 | 81 |
| 015 | 01 | 90 |
| 016 | 01 | 71 |
| 017 | 01 | 58 |
| 018 | 01 | 38 |
| 019 | 01 | 46 |
| 020 | 01 | 89 |
| 001 | 02 | 63 |
| 002 | 02 | 84 |
| 004 | 02 | 93 |
| 005 | 02 | 44 |
| 006 | 02 | 90 |
| 007 | 02 | 55 |
| 008 | 02 | 34 |
| 009 | 02 | 78 |
| 010 | 02 | 68 |
| 011 | 02 | 49 |
| 012 | 02 | 74 |
(仅显示前 30 行,完整数据见原始资料库中的第11周实训文档)
二、练习题 按单元递进:DDL → DML → 查询 → 函数 → 优化
单元3 Hive数据定义
在 Hive 里建一个名为 school 的数据库,并切换到它。 基础
提示:CREATE DATABASE + USE
查看参考答案
CREATE DATABASE IF NOT EXISTS school COMMENT '校园选课库';
USE school;建一张学生表 student_info(学号、姓名、生日、性别),用逗号分隔、文本格式存储。 基础
提示:ROW FORMAT DELIMITED FIELDS TERMINATED BY
查看参考答案
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;把 /home/hadoop/data/student_info.txt 加载到学生表,并确认行数。 基础
提示:LOAD DATA LOCAL INPATH + count(*)
查看参考答案与结果
LOAD DATA LOCAL INPATH '/home/hadoop/data/student_info.txt' INTO TABLE student_info;
SELECT count(*) FROM student_info;| cnt |
|---|
| 20 |
建一张按课程分区的成绩表 score_part,并把 score_info 的数据按 course_id 动态写入。 进阶
提示:PARTITIONED BY + 动态分区参数
查看参考答案与结果
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;| course_id | cnt |
|---|---|
| 01 | 19 |
| 02 | 19 |
| 03 | 19 |
| 04 | 12 |
| 05 | 5 |
建一张按学号分成 4 个桶的成绩表,并从中抽第 1 个桶的数据。 进阶
提示:CLUSTERED BY … INTO n BUCKETS + TABLESAMPLE
查看参考答案
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);创建一个视图 v_score_detail,包含学生姓名、课程名、老师姓名、成绩四个字段。 进阶
提示:CREATE VIEW … AS SELECT
查看参考答案与结果
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;| stu_name | course_name | tea_name | score |
|---|---|---|---|
| 彭于晏 | 语文 | 王子文 | 94 |
| 胡歌 | 语文 | 王子文 | 74 |
| 刘德华 | 语文 | 王子文 | 85 |
| 唐国强 | 语文 | 王子文 | 64 |
| 陈道明 | 语文 | 王子文 | 71 |
单元4 Hive数据操作
用查询结果建一张“男生信息表” stu_male。 基础
提示:CREATE TABLE + INSERT … SELECT
查看参考答案与结果
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;| male_count |
|---|
| 11 |
单元5 Hive数据查询
查出所有女生的学号、姓名和生日。 基础
提示:WHERE sex = '女'
WHERE sex = '女'。注意字符串用英文单引号。查看参考答案与结果
SELECT stu_id, stu_name, birthday FROM student_info WHERE sex = '女';| stu_id | stu_name | birthday |
|---|---|---|
| 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 |
查出姓“刘”的学生姓名。 基础
提示:LIKE '刘%'
LIKE '刘%':% 代表"任意多个字符"。想匹配一个字用下划线 _。查看参考答案与结果
SELECT stu_name FROM student_info WHERE stu_name LIKE '刘%';| stu_name |
|---|
| 刘德华 |
| 刘亦菲 |
统计每门课程的选课人数和平均分(平均分保留两位小数),按课程号升序。 基础
提示:GROUP BY + count + round(avg(),2)
count(*),平均分用 round(avg(score),2)。查看参考答案与结果
SELECT course_id, count(*) AS 人数, round(avg(score), 2) AS 平均分
FROM score_info GROUP BY course_id ORDER BY course_id;| course_id | cnt | avg_score |
|---|---|---|
| 01 | 19 | 67.16 |
| 02 | 19 | 59.63 |
| 03 | 19 | 69.42 |
| 04 | 12 | 63.42 |
| 05 | 5 | 74.6 |
找出平均分高于 65 分的课程,按平均分降序显示课程名与平均分。 进阶
提示:子查询/CTE + JOIN course_info
HAVING avg(score) > 65 过滤;② 再和 course_info 关联取出课程名。用 WITH 写最清楚。查看参考答案与结果
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;| course_name | avg_score |
|---|---|
| 音乐 | 74.6 |
| 英语 | 69.42 |
| 语文 | 67.16 |
按总分降序找出前 5 名学生,显示姓名和总分。 进阶
提示:GROUP BY + sum + JOIN + ORDER BY DESC LIMIT
GROUP BY 学生求 sum(score),再 JOIN 学生表拿姓名,最后 ORDER BY 总分 DESC LIMIT 5。查看参考答案与结果
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;| stu_name | total |
|---|---|
| 唐国强 | 377 |
| 郭德纲 | 371 |
| 胡歌 | 345 |
| 刘德华 | 326 |
| 周冬雨 | 325 |
查出每门课的任课老师是谁(课程名 + 老师名)。 进阶
提示:course_info JOIN teacher_info
tea_id 内连接即可,不需要成绩表。查看参考答案与结果
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;| course_name | tea_name |
|---|---|
| 语文 | 王子文 |
| 数学 | 张高数 |
| 英语 | 刘丽英 |
| 体育 | 李体音 |
| 音乐 | 李体音 |
找出所有没有参加 05 号课程考试的学生。 进阶
提示:LEFT JOIN + IS NULL
sc.course_id = '05' 要写在 ON 里而不是 WHERE 里,再加 WHERE sc.stu_id IS NULL 取差集。查看参考答案与结果
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;| stu_id | stu_name |
|---|---|
| 001 | 彭于晏 |
| 002 | 胡歌 |
| 003 | 周杰伦 |
| 004 | 刘德华 |
| 006 | 陈道明 |
| 008 | 吴京 |
| 010 | 于谦 |
| 011 | 潘长江 |
| 012 | 杨紫 |
| 013 | 蒋欣 |
| 014 | 赵丽颖 |
| 016 | 周冬雨 |
统计男、女生的平均分各是多少。 进阶
提示:JOIN 后再 GROUP BY sex
GROUP BY s.sex。分组列写 s.sex 更稳妥。查看参考答案与结果
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;| sex | avg_score | cnt |
|---|---|---|
| 女 | 61.53 | 34 |
| 男 | 69.25 | 40 |
单元6 Hive函数应用
用 CASE WHEN 把 01 号课程的成绩分成 优秀(≥90)/良好(≥80)/及格(≥60)/不及格 四档,并统计各档人数。 进阶
提示:CASE WHEN + 外层 GROUP BY
查看参考答案与结果
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;| lv | cnt |
|---|---|
| 不及格 | 7 |
| 及格 | 6 |
| 良好 | 4 |
| 优秀 | 2 |
用窗口函数取每门课程成绩前 3 名的学生(显示课程、学号、成绩、名次)。 高阶
提示:rank() OVER (PARTITION BY … ORDER BY …)
rank() OVER (PARTITION BY course_id ORDER BY score DESC),外层再筛 rk <= 3。查看参考答案与结果
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;| course_id | stu_id | score | rk |
|---|---|---|---|
| 01 | 001 | 94 | 1 |
| 01 | 015 | 90 | 2 |
| 01 | 020 | 89 | 3 |
| 02 | 004 | 93 | 1 |
| 02 | 006 | 90 | 2 |
| 02 | 016 | 89 | 3 |
| 03 | 005 | 99 | 1 |
| 03 | 013 | 93 | 2 |
| 03 | 019 | 93 | 2 |
| 04 | 002 | 100 | 1 |
| 04 | 016 | 94 | 2 |
| 04 | 005 | 85 | 3 |
把学生姓名转成大写,并拼上性别,输出形如 “彭于晏(男)” 的标签。 进阶
提示:upper + concat
concat(upper(stu_name), '(', sex, ')')。查看参考答案与结果
SELECT concat(upper(stu_name), '(', sex, ')') AS 标签
FROM student_info LIMIT 5;| label |
|---|
| 彭于晏(男) |
| 胡歌(男) |
| 周杰伦(男) |
| 刘德华(男) |
| 唐国强(男) |
统计每门课程成绩的标准差(保留 2 位小数),并按标准差降序。 高阶
提示:stddev() 或 stddev_samp()
stddev(score),和 avg 一样配合 GROUP BY 使用。查看参考答案
SELECT course_id,
count(*) AS 人数,
round(stddev(score), 2) AS 标准差
FROM score_info
GROUP BY course_id
ORDER BY 标准差 DESC;单元7 Hive性能优化
假设 course_id='01' 的数据量特别大导致数据倾斜,写出两阶段聚合的 SQL。 高阶
提示:加盐 → 局部聚合 → 去盐 → 全局聚合
GROUP BY course_id, cast(rand()*10 AS INT) 先做局部计数(加盐打散);② 外层 GROUP BY course_id 把局部结果 sum 起来(去盐)。查看参考答案与结果
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;| course_id | cnt |
|---|---|
| 01 | 19 |
| 02 | 19 |
| 03 | 19 |
| 04 | 12 |
| 05 | 5 |
建一张 ORC 格式、Snappy 压缩的成绩表,并说明它比 TEXTFILE 快在哪。 进阶
提示:STORED AS ORC + TBLPROPERTIES
STORED AS ORC,再用 TBLPROPERTIES('orc.compress'='SNAPPY') 指定压缩。查看参考答案与结果
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;