挑战100天不停更hive sql 第25天 -场景题一 学生课程

?挑战100天不停更,刷爆 hive sql?

详情请点击?我的专栏?,共同学习,一起进步~

文章目录

NUM: 第25天 -场景题一 学生课程

最近两天下班一直都在整理这套场景题,一共是50题,因为后面的比较简单,二十八后面的我就省略了(最后面我把问题贴出来), 表结构和数据我都写好了,可以直接复制, 由于篇幅比较长,本来我是想在前面贴导航的,但是无奈我是markdown,所以大家只能看边上的导航去筛选了~

先简单说一下吧:
一共四张表, 学生 -成绩- 课程 - 老师 ,主要是用一些join,分组,排序等等 ,比较简单,但是问题考察的点还是比较全面的, 建议大家看看, 不建议全部都敲 , 看不懂的再敲 ,我自己写的时候写到28题的时候就没有耐心了, 每题我尽量也分了不同的写法,供大家参考!

? 如果有问题也欢迎大家留言~~

?那废话不多说了, 刷题~~?

?表结构

student(学生表)
image.png

score(成绩表)
image.png

course(课程表)
image.png

teacher(老师表)
image.png

?建表

-- 建表
create table student
(
    s_id    string,
    s_name  string,
    s_birth string,
    s_sex   string
) row format delimited fields terminated by '\t';
create table course
(
    c_id   string,
    c_name string,
    t_id   string
) row format delimited fields terminated by '\t';
create table teacher
(
    t_id   string,
    t_name string
) row format delimited fields terminated by '\t';
create table score
(
    s_id    string comment '学生id',
    c_id    string comment '课程id',
    s_score int comment '成绩'
) row format delimited fields terminated by
    '\t';

?数据准备

这里粘贴的时候很容易多出空格,加载的数据的时候一定要用文本工具看一下

vi /opt/data//student.csv
01	赵雷	1990-01-01	男
02	钱电	1990-12-21	男
03	孙风	1990-05-20	男
04	李云	1990-08-06	男
05	周梅	1991-12-01	女
06	吴兰	1992-03-01	女
07	郑竹	1989-07-01	女
08	王菊	1990-01-20	女
vi /opt/data//course.csv
01	语文	02
02	数学	01
03	英语	03
vi /opt/data//teacher.csv
01	张三
02	李四
03	王五

vi /opt/data//score.csv
01	01	80
01	02	90
01	03	99
02	01	70
02	02	60
02	03	80
03	01	80
03	02	80
03	03	80
04	01	50
04	02	30
04	03	20
05	01	76
05	02	87
06	01	31
06	03	34
07	02	89
07	03	98

?导入数据

load data local inpath '/opt/data/student.csv' into table student;
load data local inpath '/opt/data/course.csv' into table course;
load data local inpath '/opt/data/teacher.csv' into table teacher;
load data local inpath '/opt/data/score.csv' into table score;

✨1)查询"01"课程比"02"课程成绩高的学生的信息及课程分数 ?

?SQL

select stu.*
     , s.s_score
from student stu
         join score s on stu.s_id = s.s_id and c_id = '01'
         left join score s2 on s.s_id = s2.s_id and s2.c_id = '02'
where s.s_score > s2.s_score;


select stu.*
     , s.s_score
from student stu
         join score s on s.c_id = '01'
         join score s1 on s1.c_id = '02'
where stu.s_id = s.s_id
  and stu.s_id = s1.s_id
  and s.s_score > s1.s_score;

✨2)查询"01"课程比"02"课程成绩低的学生的信息及课程分数 ?

?SQL

select stu.*
     , s.s_score
from student stu
         join score s on stu.s_id = s.s_id and c_id = '01'
         left join score s2 on s.s_id = s2.s_id and s2.c_id = '02'
where s.s_score < s2.s_score;


select stu.*
     , s.s_score
from student stu
         join score s on s.c_id = '01'
         join score s1 on s1.c_id = '02'
where stu.s_id = s.s_id
  and stu.s_id = s1.s_id
  and s.s_score < s1.s_score;

✨3)查询学生成绩大于60分的学生的编号 姓名 和 平均成绩

?SQL

select s.s_id
     , s.s_name
     , tmp.avg_score
from student s
         join (select s_id
                    , round(avg(score.s_score), 2) avg_score
               from score
               group by s_id) tmp
              on tmp.s_id = s.s_id
where tmp.avg_score >= 60;

✨4)查询平均成绩小于60分的同学的学生编号和学生姓名和平均成绩:(包括有成绩的和无成绩的)

?SQL

-- 解法一
select student.s_id
     , student.s_name
     , tmp.avgScore
from student
         join (
    select score.s_id
         , round(avg(score.s_score), 2) as avgScore
    from score
    group by s_id
) as tmp
              on tmp.avgScore < 60
where student.s_id = tmp.s_id
-- 这里的无成绩的指的是在学生表中没有数据的
union all
select s.s_id
     , s.s_name
     , 0 as avgScore
from student s
where s_id not in
      (
          select distinct s_id
          from score
      )
order by s_id;

-- 解法二
select score.s_id
     , student.s_name
     , round(avg(score.s_score), 1) as avgScore
from student
         inner join score on student.s_id = score.s_id
group by score.s_id, student.s_name
having avg(score.s_score) < 60
union all
select s2.s_id
     , s2.s_name
     , 0 as avgScore
from student s2
where s2.s_id not in
      (select distinct sc2.s_id from score sc2);

✨5)查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩

?SQL

select stu.s_id
     , stu.s_name
     , count(s.c_id)                                   coures_cnt
     , `if`(sum(s.s_score) is null, 0, sum(s.s_score)) score_sum
from student stu
         left join score s on stu.s_id = s.s_id
group by stu.s_id, stu.s_name;

✨6)查询"李"姓老师的数量

?SQL

select t_id
     , t_name
     , count(1)
from teacher
where t_name like '李%'
group by t_id, t_name;

✨7)询学过"张三"老师授课的同学的信息:

?SQL

select stu.*
from student stu
         join score s on stu.s_id = s.s_id
         join course c on s.c_id = c.c_id
         join teacher t on c.t_id = t.t_id and t.t_name = "张三";

✨8)查询没学过"张三"老师授课的同学的信息:

?SQL

select stu.s_id
     , s_name
     , s_birth
     , s_sex
from student stu
         left join
     (
         select s_id
         from score
                  join course c on score.c_id = c.c_id
                  join teacher t on c.t_id = t.t_id
         where t_name = '张三'
     ) tmp on tmp.s_id = stu.s_id
where tmp.s_id IS NULL;

✨9)查询学过编号为"01"并且也学过编号为"02"的课程的同学的信息

?SQL

select student.*
from student
         join score s on student.s_id = s.s_id and s.c_id = '01'
         join score s2 on student.s_id = s2.s_id and s2.c_id = '02';

select student.*
from student
         join (select s_id from score where c_id = '01') tmp1 on student.s_id = tmp1.s_id
         join (select s_id from score where c_id = '02') tmp2 on student.s_id = tmp2.s_id;

✨10)查询学过编号为"01"但是没有学过编号为"02"的课程的同学的信息

?SQL

select student.*
from student
         join score s on student.s_id = s.s_id and s.c_id = '01'
         left join score s2 on student.s_id = s2.s_id and s2.c_id = '02'
where s2.s_id is null;

select student.*
from student
         join (select s_id from score where c_id = '01') tmp1 on student.s_id = tmp1.s_id
         left join (select s_id from score where c_id = '02') tmp2 on student.s_id = tmp2.s_id
where tmp2.s_id is null;

✨11) 询没有学全所有课程的同学的信息:

?SQL

-- 答案一:这种写法有缺陷, s_id = 8的学生在课程表中没有数据,所以查不到,答案只有三条数据
select *
from student stu
         join
     (
         select s_id
         from score
         group by s_id
         having count(c_id) < (select count(1)
                               from course)
     ) tmp on stu.s_id = tmp.s_id;
--答案二: 用left join取空值
select student.*
from student
         left join
     (
         select s_id
         from score
         group by s_id
         having count(c_id) = (select count(1) from course)
     ) tmp
     on student.s_id = tmp.s_id
where tmp.s_id is null;
-- 答案三: 先join课程总数量, 再用left join成绩表的根据学生分组,并count c_id,求不包含的学生信息
select student.*
from student
         join
     (
         select count(c_id) num1
         from course
     ) tmp1
         left join
     (
         select s_id, count(c_id) num2
         from score
         group by s_id
     ) tmp2
     on student.s_id = tmp2.s_id and tmp1.num1 = tmp2.num2
where tmp2.s_id is null;

✨12)查询至少有一门课与学号为"01"的同学所学相同的同学的信息

?SQL

-- 答案一:
select stu.*
from student stu
where stu.s_id in
      (
          -- 查询01学生学过的课程在 课程表中的所有课程,并排除学生为01的
          select distinct score.s_id
          from score
          where c_id
              in
                (
                    -- 查询学生为01所学的课程id
                    select c_id
                    from score
                    where score.s_id = '01'
                )
            and s_id != '01'
      );
-- 答案二:
select stu.*
from student stu
         inner join
     (
         -- 查询01学生学过的课程在 课程表中的所有课程,并排除学生为01的
         select distinct score.s_id
         from score
         where c_id
             in
               (
                   -- 查询学生为01所学的课程id
                   select c_id
                   from score
                   where score.s_id = '01'
               )
           and s_id != '01'
     ) tmp on stu.s_id = tmp.s_id;
-- 答案三:
select student.*
from student
         join (select c_id from score where s_id = '01') tmp1
         join score s on student.s_id = s.s_id and tmp1.c_id = s.c_id
where student.s_id not in ('01')
group by student.s_id, s_name, s_birth, s_sex;

✨13)查询和"01"号的同学学习的课程完全相同的其他同学的信息

?SQL

-- 解法一:
select student.*
from student
         join
     (
         select tmp1.s_id
         from (select s_id
                    , concat_ws(',', collect_set(c_id)) arr_cid_01
               from score
               where s_id != '01'
               group by s_id) tmp1
                  join
              (select s_id
                    , concat_ws(',', collect_set(c_id)) arr_cid_02
               from score
               where s_id = '01'
               group by s_id) tmp2
              on tmp1.arr_cid_01 = tmp2.arr_cid_02
     ) tmp3 on tmp3.s_id = student.s_id;
-- 解法二
select student.*
     , tmp1.course_id
from student
         join
     (
         select s_id, concat_ws('|', collect_set(c_id)) course_id
         from score
         group by s_id
         having s_id not in (1)
     ) tmp1
     on student.s_id = tmp1.s_id
         join
     (
         select concat_ws('|', collect_set(c_id)) course_id2 from score where s_id = 1
     ) tmp2
     on tmp1.course_id = tmp2.course_id2;

✨14)查询没学过"张三"老师讲授的任一门课程的学生姓名

?SQL

-- 解法一:
select distinct student.*
from student
         join score s on student.s_id = s.s_id
where c_id not in
      (
          select c_id
          from course
                   join teacher t on course.t_id = t.t_id and t.t_name = '张三'
      );
-- 解法二:
select distinct student.*
from student
         join score s on student.s_id = s.s_id
         join course c on s.c_id = c.c_id
         join teacher t on c.t_id = t.t_id and t.t_name != '张三';
-- 解法三:(推荐)
select student.*
from student
         left join
     (
         select s_id
         from score
                  join
              (
                  select c_id
                  from course
                           join teacher on course.t_id = teacher.t_id and t_name = '张三'
              ) tmp2 on score.c_id = tmp2.c_id
     ) tmp
     on student.s_id = tmp.s_id
where tmp.s_id is null;

✨15)查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩

?SQL

-- 解题一:
select student.*
     , s_cnt
     , avg_score
from student
         join
     (
         select s_id
              , count(s_id) s_cnt
         from score
         where score.s_score < 60
         group by s_id
         having s_cnt > 1
     ) tmp on student.s_id = tmp.s_id
         left join
     (
         select s_id, round(AVG(score.s_score)) avg_score
         from score
         group by s_id
     ) tmp1 on tmp1.s_id = student.s_id;

✨16)检索"01"课程分数小于60,按分数降序排列的学生信息

?SQL

select student.*
     , s_score
from student
         join
     (
         select s_id
              , c_id
              , s_score
         from score
         where c_id = "01"
           and score.s_score < 60
     ) tmp on student.s_id = tmp.s_id
order by s_score desc;

✨17)按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩

?SQL

select a.s_id,
       tmp1.s_score             as chinese,
       tmp2.s_score             as math,
       tmp3.s_score             as english,
       round(avg(a.s_score), 2) as avgScore
from score a
         left join (select s_id, s_score from score s1 where c_id = '01') tmp1 on tmp1.s_id = a.s_id
         left join (select s_id, s_score from score s2 where c_id = '02') tmp2 on tmp2.s_id = a.s_id
         left join (select s_id, s_score from score s3 where c_id = '03') tmp3 on tmp3.s_id = a.s_id
group by a.s_id, tmp1.s_score, tmp2.s_score, tmp3.s_score
order by avgScore desc;

✨18)查询各科成绩最高分、最低分和平均分:以如下形式显示:


课程ID,课程name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率
及格为>=60,中等为:70-80,优良为:80-90,优秀为:>90

?SQL

select c.c_id
     , c.c_name
     , max(score.s_score)                                                              max_score
     , min(score.s_score)                                                              min_score
     , round(round(avg(score.s_score)), 2)                                             avg_score
     , round(count(case when s_score >= 60 then 1 end) / count(1), 2)                  pass
     , round(count(case when s_score < 60 then 1 end) / count(1), 2)                   fail
     , round(count(case when s_score >= 70 and s_score < 80 then 1 end) / count(1), 2) mid
     , round(count(case when s_score >= 80 and s_score < 90 then 1 end) / count(1), 2) good
     , round(count(case when s_score >= 90 then 1 end) / count(1), 2)                  verygood
     , count(1)                                                                        score_cnt
from score
         join course c on score.c_id = c.c_id
group by c.c_id, c.c_name;

✨19)按各科成绩进行排序,并显示排名: (这里不能用row_number(),得用rank())

?SQL

-- 解题1:
select s1.*, rank() over (order by s1.s_score desc) Ranking
from score s1
where s1.c_id = '01'
union all
select s1.*, rank() over (order by s1.s_score desc) Ranking
from score s1
where s1.c_id = '02'
union all
select s1.*, rank() over (order by s1.s_score desc) Ranking
from score s1
where s1.c_id = '03';

-- 解题二: 直接用partition by 不香吗??
select s1.*, rank() over (partition by s_id order by s1.s_score desc) Ranking
from score s1;

✨20)查询学生的总成绩并进行排名:

?SQL

select score.s_id
     , s_name
     , sum(s_score)                                   sumscore
     , row_number() over (order by sum(s_score) desc) Ranking
from score,
     student
where score.s_id = student.s_id
group by score.s_id, s_name
order by sumscore desc;

select student.s_name
     , sum
     , rank() over (order by sum desc)
from student
         join
     (
         select s_id
              , sum(score.s_score) sum
         from score
         group by s_id
     ) tmp on tmp.s_id = student.s_id;

✨21)查询不同老师所教不同课程平均分从高到低显示:

?SQL

select course.c_id
     , course.t_id
     , t_name
     , round(avg(s_score), 2) as avgscore
from course
         join teacher on teacher.t_id = course.t_id
         join score on course.c_id = score.c_id
group by course.c_id, course.t_id, t_name
order by avgscore desc;

select course.c_id
     , course.t_id
     , t_name
     , round(avg(s_score), 2) as avgscore
from course
   , teacher
   , score
where teacher.t_id = course.t_id
  and course.c_id = score.c_id
group by course.c_id, course.t_id, t_name
order by avgscore desc;

✨22)查询所有课程的成绩第2名到第3名的学生信息及该课程成绩

?SQL

-- 问题二十二:查询所有课程的成绩第2名到第3名的学生信息及该课程成绩
-- 解题一:
-- select tmp1.*
-- from (select * from score where c_id = '01' order by s_score desc limit 3) tmp1
-- order by s_score asc
-- limit 2
-- union all
-- select tmp2.*
-- from (select * from score where c_id = '02' order by s_score desc limit 3) tmp2
-- order by s_score asc
-- limit 2
-- union all
-- select tmp3.*
-- from (select * from score where c_id = '03' order by s_score desc limit 3) tmp3
-- order by s_score asc
-- limit 2;

-- 解题二:
select *
from (
         select c_name
              , s2.s_name
              , s.s_score
              , row_number() over (partition by course.c_id order by s_score desc) course_score_rank
         from course
                  join score s on course.c_id = s.c_id
                  join student s2 on s.s_id = s2.s_id
     ) tmp
where course_score_rank in (2, 3);

✨23)统计各科成绩各分数段人数:课程编号,课程名称,[100-85],[85-70],[70-60],[0-60]及所占百分比

?SQL

select c.c_id
     , c.c_name
     , tmp1.s0_60
     , tmp1.percentum
     , tmp2.s60_70
     , tmp2.percentum
     , tmp3.s70_85
     , tmp3.percentum
     , tmp4.s85_100
     , tmp4.percentum
from course c
         join(
    select c_id
         , sum(case when s_score < 60 then 1 else 0 end)                               as s0_60
         , round(100 * sum(case when s_score < 60 then 1 else 0 end) / count(c_id), 2) as percentum
    from score
    group by c_id
) tmp1 on tmp1.c_id = c.c_id
         left join
     (
         select c_id
              , sum(case when s_score < 70 and s_score >= 60 then 1 else 0 end)                             as s60_70
              , round(100 * sum(case when s_score < 70 and s_score >= 60 then 1 else 0 end) / count(c_id),) as percentum
         from score
         group by c_id
     ) tmp2
     on tmp2.c_id = c.c_id
         left join
     (
         select c_id
              , sum(case when s_score < 85 and s_score >= 70 then 1 else 0 end) as s70_85
              , round(100 * sum(case when s_score < 85 and s_score >= 70 then 1 else 0 end) / count(c_id)
                      2)                                                        as percentum
         from score
         group by c_id
     ) tmp3
     on tmp3.c_id = c.c_id
         left join
     (
         select c_id
              , sum(case when s_score >= 85 then 1 else 0 end)                               as s85_100
              , round(100 * sum(case when s_score >= 85 then 1 else 0 end) / count(c_id), 2) as percentum

         from score
         group by c_id
     ) tmp4 on tmp4.c_id = c.c_id;

✨24)查询学生平均成绩及其名次:

?SQL

select tmp.*
     , row_number() over (order by tmp.avgScore desc) Ranking
from (
         select student.s_id
              , student.s_name
              , round(avg(score.s_score), 2) as avgScore
         from student
                  join score on student.s_id = score.s_id
         group by student.s_id, student.s_name
     ) tmp
order by avgScore desc;

✨25)查询各科成绩前三名的记录

?SQL

-- –课程id为01的前三名
select score.c_id, course.c_name, student.s_name, s_score
from score
         join student on student.s_id = score.s_id
         join course on score.c_id = '01' and course.c_id = score.c_id
order by s_score desc
limit 3;

-- –课程id为02的前三名
select score.c_id, course.c_name, student.s_name, s_score
from score
         join student on student.s_id = score.s_id
         join course on score.c_id = '02' and course.c_id = score.c_id
order by s_score desc
limit 3;
-- –课程id为03的前三名
select score.c_id, course.c_name, student.s_name, s_score
from score
         join student on student.s_id = score.s_id
         join course on score.c_id = '03' and course.c_id = score.c_id
order by s_score desc
limit 3;

✨26)查询每门课程被选修的学生数

?SQL

select c.c_id
     , c.c_name
     , tmp.number
from course c
         join (select c_id, count(1) as number from score where score.s_score < 60 group by score.c_id) tmp
              on tmp.c_id = c.c_id;

✨27)查询出只有两门课程的全部学生的学号和姓名:

?SQL

select st.s_id
     , st.s_name
from student st
         join
     (
         select s_id
         from score
         group by s_id
         having count(c_id) = 2
     ) tmp
     on st.s_id = tmp.s_id;

✨28)查询男生、女生人数:

?SQL

select tmp1.man, tmp2.women
from (select count(1) as man from student where s_sex = '男') tmp1,
     (select count(1) as women from student where s_sex = '女') tmp2;

✨29 - 50) 这里就不写了,感兴趣可以自己写写哦!

?SQL

-- 问题 29、查询名字中含有"风"字的学生信息:
-- 问题 30、查询同名同性学生名单,并统计同名人数:
-- 问题 31、查询1990年出生的学生名单:
-- 问题 32、查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列:
-- 问题 33、查询平均成绩大于等于85的所有学生的学号、姓名和平均成绩:
-- 问题 34、查询课程名称为"数学",且分数低于60的学生姓名和分数:
-- 问题 35、查询所有学生的课程及分数情况:
-- 问题 查询任何一门课程成绩在70分以上的学生姓名、课程名称和分数:
-- 问题 37、查询课程不及格的学生:
-- 问题 38、查询课程编号为01且课程成绩在80分以上的学生的学号和姓名:
-- 问题 39、求每门课程的学生人数:
-- 问题 40、查询选修"张三"老师所授课程的学生中,成绩最高的学生信息及其成绩:
-- 问题 查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩: select distinct
-- 问题 42、查询每门课程成绩最好的前三名:
-- 问题 43、统计每门课程的学生选修人数(超过5人的课程才统计): 要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列
-- 问题 44、检索至少选修两门课程的学生学号:
-- 问题 查询选修了全部课程的学生信息
-- 问题 查询选修了全部课程的学生信息
-- 问题 查询本周过生日的学生:
-- 问题 查询本周过生日的学生:
-- 问题 查询12月份过生日的学生:


关于sql的规范问题

尽量把逗号放在前面,为什么?
1,方便排查,不会遗漏逗号
2,方便注释,可以单行直接注释,不用再改逗号
3,排版看起来更紧密,我用datagrip快捷键ctrl + shift + L可以快速缩进
4,特别用datagrip进行快速复制一行的时候,只需要Ctrl + D不需要关注逗号,写起来很快


版权声明:本文为weixin_38221481原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。