Hive 的重要性不必多说,离线批处理的王者,Hive 用来做数据分析,SQL 基础必须十分牢固。
这里建4张表,下面的练习题都用这些数据。
- -- 创建学生表
- 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;
-
- -- 创建课程表
- 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;
-
- -- 创建老师表
- 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;
-
- -- 创建分数表
- 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;
student_info.txt
- 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,女
teacher_info.txt
- 1001,张高数
- 1002,李体音
- 1003,王子文
- 1004,刘丽英
course_info.txt
- 01,语文,1003
- 02,数学,1001
- 03,英语,1004
- 04,体育,1002
- 05,音乐,1002
score_info.txt
- 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
- 013,02,35
- 014,02,39
- 015,02,48
- 016,02,89
- 017,02,34
- 018,02,58
- 019,02,39
- 020,02,59
- 001,03,79
- 002,03,87
- 004,03,89
- 005,03,99
- 006,03,59
- 007,03,70
- 008,03,39
- 009,03,60
- 010,03,47
- 011,03,70
- 012,03,62
- 013,03,93
- 014,03,32
- 015,03,84
- 016,03,71
- 017,03,55
- 018,03,49
- 019,03,93
- 020,03,81
- 001,04,54
- 002,04,100
- 004,04,59
- 005,04,85
- 007,04,63
- 009,04,79
- 010,04,34
- 013,04,69
- 014,04,40
- 016,04,94
- 017,04,34
- 020,04,50
- 005,05,85
- 007,05,63
- 009,05,79
- 015,05,59
- 018,05,87
加载数据到 Hive 的数据源目录
- load data local inpath '/opt/module/hive-3.1.2/datas/student_info.txt' into table student_info;
- load data local inpath '/opt/module/hive-3.1.2/datas/teacher_info.txt' into table teacher_info;
- load data local inpath '/opt/module/hive-3.1.2/datas/course_info.txt' into table course_info;
- load data local inpath '/opt/module/hive-3.1.2/datas/score_info.txt' into table score_info;
重点就是一个 where ,可能涉及到一点多表联结。
简单的可以用 like 配合 % 和 _ ,复杂的可以使用 Hive 扩展的 rlike 配合正则表达式。
- -- 查询姓名中带“冰”的学生名单
- select * from student_info where stu_name like '%冰%';
- -- 查询姓“王”老师的个数
- select count(*) from teacher_info where tea_name like '王%';
- -- 或者
- select count(*) from teacher_info where tea_name rlike '^王';
通过 course_id 联结两张表,找到不及格的成绩所对应的课程信息。
- select c.* from course_info c
- join score_info s on c.course_id = s.course_id
- where c.course_id = 4 and s.score < 60
- order by s.score desc;
- select stu.*,s.score from student_info stu
- join (
- select * from score_info
- where course_id =
- (select course_id from course_info where course_name = '数学')
- )s
- on s.stu_id = stu.stu_id
- where s.score < 60
- order by stu.stu_id;
这里需要注意的是,聚合函数通常和 group by 配合使用!表示分组再做聚合处理。
- --查询编号为“02”的课程的总成绩
- select course_id,sum(score) from score_info where course_id = 02
- group by course_id;
select count(distinct stu_id) from score_info;
重点就是一个 group by。
不同的科目对应不同的 course_id ,所以我们用 group by course_id。
- -- 同样这里有聚合函数配合 group by 来使用
- select course_id,max(score) max,min(score) min from score_info
- group by course_id;
- select course_id, count(stu_id) from score_info
- group by course_id;
- -- 查询男生、女生人数
- select sex,count(stu_id) from student_info
- group by sex;
重点就是 group by 之后的条件判断语句用 having。
这里需要先分组后判断,所以不能用 where,因为 group by 后面的条件语句只能是 having。
- -- 这里需要分组后再判断条件
- select stu_id,avg(score) avg_score from score_info
- group by stu_id
- having avg_score>60;
- -- 查询至少选修四门课程的学生学号
- select stu_id,count(course_id) cnt from score_info
- group by stu_id
- having cnt>=4;
这里用到一个没用过的函数 substr() ,需要记忆一下。
- select t1.first_name,count(stu_id) cnt from (
- select *,substr(stu_name,0,1) first_name from student_info
- )t1
- group by t1.first_name
- having cnt>=2;
多重排序判断直接逗号隔开即可。
- -- 查询每门课程的平均成绩,结果按平均成绩升序排序,平均成绩相同时,按课程号降序排列
- select course_id,avg(score) avg_score from score_info
- group by course_id
- order by avg_score,course_id desc ;
- -- 统计参加考试人数大于等于15的学科
- select course_id,count(*) cnt from score_info
- group by course_id
- having cnt>=15;
- -- 查询学生的总成绩并按照总成绩降序排序
- select stu_id,sum(score) sum_score from score_info
- group by stu_id
- order by sum_score desc;
这里用到了 Hive 中的 if 语句,它的语法是:
IF(condition, true_value, false_value)
其中,condition是要评估的条件,true_value是当条件为真时要返回的值,false_value是当条件为假时要返回的值。
比如;
- SELECT name, age, IF(age >= 18, 'Adult', 'Minor') AS age_group
- FROM users;
此外,还可以使用多重 if 嵌套语句:
- SELECT name, age,
- IF(age >= 18 AND gender = 'Male', 'Adult Male',
- IF(age >= 18 AND gender = 'Female', 'Adult Female',
- IF(age < 18 AND gender = 'Male', 'Minor Male', 'Minor Female'))) AS age_group
- FROM users;
本题:这里的反引号是引用的作用,这里代表的是 列名。
- select s.stu_id,
- sum(if(c.course_name='语文',score,0)) `语文`,
- sum(if(c.course_name='数学',score,0)) `数学`,
- sum(if(c.course_name='英语',score,0)) `英语`,
- count(*) `有效课程数`,
- avg(s.score) `平均成绩`
- from score_info s
- join course_info c on s.course_id = c.course_id
- group by s.stu_id
- order by `平均成绩` desc ;
有点复杂,需要好好理解掌握。
- -- 查询一共参加三门课程且其中一门为语文课程的学生的id和姓名
- select t2.stu_id,s.stu_name from (
- select t1.stu_id from (
- select stu_id,course_id from score_info where stu_id in (
- select stu_id from score_info
- where course_id = '01'
- ) -- 筛选出有语文成绩的学生的id
- ) t1
- group by t1.stu_id
- having count(t1.course_id)=3
- ) t2
- join student_info s on t2.stu_id = s.stu_id;
我们根据 stu_id 把每个学生的成绩信息聚合在一起。然后巧妙的使用了 if 语句来判断是否有不及格的科目,如果>=60分,结果+1,最后用 sum 函数统计出结果,如果 sum 等于0,则说明全部不及格。
- -- 查询所有课程成绩均小于60分的学生的学号、姓名
- select t1.stu_id,s.stu_name from(
- select stu_id,sum(if(score>=60,1,0)) flag from score_info
- group by stu_id
- having flag=0
- ) t1
- join student_info s on s.stu_id = t1.stu_id;
这里需要注意:
- -- 查询没有学全所有课的学生的学号、姓名
- select stu_id, stu_name
- from student_info
- where stu_id not in (
- select stu_id
- from score_info
- group by stu_id
- having count(distinct course_id) = (select count(distinct course_id) from course_info)
- );
这里同样需要注意:当子查询的结果是多行值时,用 in 而不是 = !
- -- 查询出只选修了三门课程的全部学生的学号和姓名
- select stu_id,stu_name from student_info where stu_id in (
- select stu_id from score_info
- group by stu_id
- having count(course_id)=3
- );
- -- 查询有两门以上的课程不及格的同学的姓名及其平均成绩
- select stu_name,avg_score from student_info st join (
- select stu_id,avg(score) avg_score from score_info
- group by stu_id
- having sum(if(score<60,1,0))>=2) t1
- on st.stu_id=t1.stu_id;
- -- 查询所有学生的学号、姓名、选课数、总成绩
- select t1.stu_id,s.stu_name,cnt,sum_score from (
- select stu_id,count(course_id) cnt,sum(score) sum_score from score_info
- group by stu_id)t1 join student_info s
- on t1.stu_id=s.stu_id;
- -- 查询平均成绩大于85的所有学生的学号、姓名和平均成绩
- select t1.stu_id,s.stu_name,avg from (
- select stu_id,avg(score) avg from score_info
- group by stu_id
- having avg>85)t1 join student_info s
- on t1.stu_id=s.stu_id;
- -- 查询学生的选课情况:学号,姓名,课程号,课程名称
- select t1.stu_id,s.stu_name,t1.course_id,c.course_name from(
- select stu_id,course_id from score_info)t1
- join student_info s
- on t1.stu_id=s.stu_id
- join course_info c
- on c.course_id=t1.course_id;
输出结果明显按照科目分开, 前几行都是选语文的学生信息。
或者
- -- 查询学生的选课情况:学号,姓名,课程号,课程名称
- select t1.stu_id,s.stu_name,t1.course_id,c.course_name from(
- select stu_id,course_id from score_info
- group by stu_id, course_id)t1
- join student_info s
- on t1.stu_id=s.stu_id
- join course_info c
- on c.course_id=t1.course_id;
这里的输出结果明显按照姓名分开,前几行都是同一个学生的选课信息(这里的 group by要么指定两个字段(即我们要查询的 stu_id 和 course_id),要么就不需要 group by)。
- -- 查询出每门课程的及格人数和不及格人数
- select c.course_name,`及格人数`,`不及格人数` from (
- select course_id,sum(if(score>=60,1,0)) `及格人数`,sum(if(score<60,1,0)) `不及格人数` from score_info
- group by course_id)t1
- join course_info c
- on t1.course_id=c.course_id;
这里需要注意如果查询结果中没有用到聚合函数就少用 group by,因为group by会触发生成 mapreduce 程序;能用 where 最好,因为 where 不会触发产生 mapreduce 程序;where 可以秒出结果,而 group by需要好多秒。
- -- 查询课程编号为03且课程成绩在80分以上的学生的学号和姓名及课程信息
- select t1.stu_id,s.stu_name,c.course_name from (
- select stu_id,course_id from score_info
- where course_id=03 and score>80)t1
- join course_info c
- on t1.course_id=c.course_id
- join student_info s
- on t1.stu_id=s.stu_id;
- -- 课程编号为"01"且课程分数小于60,按分数降序排列的学生信息
- select s.*,t1.score from student_info s join (
- select stu_id,score from score_info
- where course_id=01 and score<60)t1
- on s.stu_id=t1.stu_id
- order by t1.score desc;
- -- 查询所有课程成绩在70分以上的学生的姓名、课程名称和分数,按分数升序排列
- select s.stu_id,s.stu_name,c.course_name,s2.score from student_info s
- join (
- select stu_id,sum(if(score>=70,0,1)) flag from score_info
- group by stu_id
- having flag=0) t1
- on s.stu_id=t1.stu_id
- left join score_info s2
- on s.stu_id=s2.stu_id
- left join course_info c
- on s2.course_id=c.course_id;
- -- 查询该学生不同课程的成绩相同的学生编号、课程编号、学生成绩
- select sc1.stu_id,sc1.course_id,sc1.score from score_info sc1
- join score_info sc2
- on sc1.stu_id=sc2.stu_id
- and sc1.course_id <> sc2.course_id
- and sc1.score=sc2.score;
- -- 查询课程编号为“01”的课程比“02”的课程成绩高的所有学生的学号
- select s1.stu_id from (
- select stu_id,course_id,score from score_info sc1
- where sc1.course_id=01) s1
- join (
- select sc2.stu_id,sc2.course_id,sc2.score from score_info sc2
- where sc2.course_id=02) s2
- on s1.stu_id=s2.stu_id
- where s1.score>s2.score;
- -- 查询学过编号为“01”的课程并且也学过编号为“02”的课程的学生的学号、姓名
- select s.stu_id,s.stu_name from(
- select stu_id,sum(if(course_id=01,1,0)+if(course_id=02,1,0)) sumOfCourse from score_info
- group by stu_id)t1
- join student_info s
- on t1.stu_id=s.stu_id
- where sumOfCourse=2;
- -- 查询学过“李体音”老师所教的所有课的同学的学号、姓名
- select t1.stu_id,si.stu_name
- from(
- select stu_id from score_info si
- where course_id in
- (
- select course_id from course_info c
- join teacher_info t
- on c.tea_id = t.tea_id
- where tea_name='李体音' --李体音教的所有课程
- )
- group by stu_id
- having count(*)=2 --学习所有课程的学生
- )t1
- left join student_info si
- on t1.stu_id=si.stu_id;
- select
- t1.stu_id,
- si.stu_name
- from
- (
- select
- stu_id
- from score_info si
- where course_id in
- (
- select
- course_id
- from course_info c
- join teacher_info t
- on c.tea_id = t.tea_id
- where tea_name='李体音'
- )
- group by stu_id
- )t1
- left join student_info si
- on t1.stu_id=si.stu_id;
- select
- stu_id,
- stu_name
- from student_info
- where stu_id not in
- (
- select
- stu_id
- from score_info si
- where course_id in
- (
- select
- course_id
- from course_info c
- join teacher_info t
- on c.tea_id = t.tea_id
- where tea_name='李体音'
- )
- group by stu_id
- );
- select
- si.stu_id,
- si.stu_name
- from score_info sc
- join student_info si
- on sc.stu_id = si.stu_id
- where sc.course_id in
- (
- select
- course_id
- from score_info
- where stu_id='001' --001的课程
- ) and sc.stu_id <> '001' --排除001学生
- group by si.stu_id,si.stu_name;
- -- 按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩
- select stu_name,course_name,sc.score,t1.avg_score from score_info sc
- join student_info si
- on sc.stu_id=si.stu_id
- join course_info ci
- on sc.course_id=ci.course_id
- join(
- select stu_id,avg(score) avg_score from score_info
- group by stu_id,course_id)t1
- on sc.stu_id=t1.stu_id
- order by t1.avg_score desc;
本想着跳过这些题目的,但是最后还是刷了一遍,期间确实发现了很多基础的不足,总之这次刷题收获满满。