经典SQL练习50题( Mysql 版)

基于别人整理的习题,自己进行了练习并进行了整理。与原作者的相比,此版本的答案用到了一些 排序函数如 ROW_NUMBER DENSE_RANK 等!

IDEA: DataGrip

/*
学生表:
Student(s_id,s_name,s_birth,s_sex)
学生编号,学生姓名, 出生年月,学生性别
*/
create table Student
(
    s_id    varchar(20),
    s_name  varchar(20) NOT NULL default '',
    s_birth varchar(20) NOT NULL DEFAULT '',
    s_sex   varchar(10) NOT NULL DEFAULT '',
    PRIMARY KEY (s_id)

);

insert into student
values ('01', '赵雷', '1990-01-01', '男');

insert into student
values ('02', '钱电', '1990-12-21', '男');

insert into student(s_id, s_name, s_birth, s_sex)
values ('04', '李云', '1990-08-06', '男'),
       ('05', '周梅', '1991-12-01', '女'),
       ('06', '吴兰', '1992-03-01', '女'),
       ('07', '郑竹', '1989-07-01', '女'),
       ('08', '王菊', '1990-01-20', '女');

select database();
show tables;

/*
教师表:
Teacher(t_id,t_name)
教师编号,教师姓名
*/
CREATE TABLE Teacher
(
    t_id   varchar(20),
    t_name varchar(20) NOT NULL default '',
    PRIMARY KEY (t_id)
);

insert into teacher
values ('01', '张三'),
       ('02', '李四'),
       ('03', '王五');
/*
成绩表:
Score(s_id,c_id,s_s_score)
学生编号,课程编号,分数
*/
CREATE TABLE Score
(
    s_id    varchar(20),
    c_id    varchar(10),
    s_score int(3),
    PRIMARY KEY (s_id, c_id)
);

insert into Score
values ('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);

/*
课程表:
Course(c_id,c_name,t_id)
课程编号, 课程名称, 教师编号
*/
create table Course
(
    c_id   varchar(20),
    c_name varchar(20) not null default '',
    t_id   varchar(20) not null,
    primary key (c_id)
);

insert into course
values ('01', '语文', '02'),
       ('02', '数学', '01'),
       ('03', '英语', '03');


-- 1、查询课程编号为“01”的课程比“02”的课程成绩高的所有学生的学号(重点)
select s.*, sc1.s_score as 01_score, sc2.s_score as 02_score
from student s
         join score sc1
              on s.s_id = sc1.s_id
         join score sc2
              on sc1.s_id = sc2.s_id
where sc1.c_id = '01'
  and sc2.c_id = '02'
  and sc1.s_score > sc2.s_score;

-- test 两个相同的表进行连接得到的结果
select s.*, sc1.s_score as 01_score, sc2.s_score as 02_score
from student s
         join score sc1
              on s.s_id = sc1.s_id
         join score sc2
              on sc1.s_id = sc2.s_id;

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

-- 3、查询平均成绩大于60分的学生的学号和平均成绩
select s_id, avg(s_score) avg
from score
group by s_id
having avg(s_score) > 60;

-- 3、2 所有成绩小于60分的学生信息
select *
from student
where s_id not in (select s_id from score where s_score > 60);


-- 4、查询平均成绩小于60分的学生的学号和平均成绩,考虑没参加考试的情况
select s.s_id, s.s_name, avg(ifnull(sc.s_score, 0)) as avg
from student s
         left join score sc
                   on s.s_id = sc.s_id
group by s.s_id
having avg < 60;

-- 5、查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩
select s.s_id, s.s_name, count(sc.c_id) 选课数, sum(sc.s_score) 总分
from student s
         left join score sc
                   on sc.s_id = s.s_id
group by s.s_id;


-- 6、查询"李"姓老师的数量
select count(t_id)
from teacher
where t_name like '李%';


-- 7、查询学过“张三”老师所教的所有课的同学的学号、姓名(重点)
select s_id, s_name
from student
where s_id in (
    select sc.s_id
    from score sc
             join course c
                  on sc.s_id = c.c_id
             join teacher t
                  on c.t_id = t.t_id
    where t.t_name = '张三'
);

-- 8、查询没学过“张三”老师课的学生的学号、姓名(重点)
select s_id, s_name
from student
where s_id not in (
    select sc.s_id
    from score sc
             join course c
                  on sc.c_id = c.c_id
             join teacher t
                  on c.c_id = t.t_id
    where t.t_name = '张三'
);

-- 9、查询学过编号为“01”的课程并且也学过编号为“02”的课程的学生的学号、姓名(重点)
select s.s_id, s.s_name
from score sc
         join student s
              on s.s_id = sc.s_id
where sc.c_id = '02'
  and s.s_id in (
    select s_id
    from score
    where c_id = '01'
);

-- 10、查询学过编号为“01”的课程但没有学过编号为“02”的课程的学生的学号、姓名(重点)
select s.s_id, s.s_name
from score sc
         join student s
              on s.s_id = sc.s_id
where sc.s_id = '01'
  and s.s_id not in (
    select s_id
    from score
    where c_id = '02'
);

-- 11、查询没有学全所有课的学生的学号、姓名(重点)
select *
from student
where s_id not in (
    select s_id
    from student
    where s_id in (select s_id from score where c_id = '01')
      and s_id in (select s_id from score where c_id = '02')
      and s_id in (select s_id from score where c_id = '03')
);

select s.*
from student s
         left join score sc
                   on s.s_id = sc.s_id
group by s.s_id
having count(sc.s_id) < (select count(c_id) from course);


-- 12、查询至少有一门课与学号为“01”的学生所学课程相同的学生的学号和姓名(重点)
select distinct s.s_id, s.s_name
from student s
         left join score sc
                   on s.s_id = sc.s_id
where sc.c_id in (
    select c_id
    from score
    where s_id = '01'
)
  and s.s_id != '01';

-- 13、查询和“01”号同学所学课程完全相同的其他同学的学号(重点)
select s_id
from score
where s_id <> '01'
group by s_id
having group_concat(c_id) = (
    select GROUP_CONCAT(c_id)
    from score
    where s_id = '01'
);

-- 14、查询没学过"张三"老师讲授的任一门课程的学生姓名 (重点,能做出来)
select s_name
from student
where s_id not in (
    select s_id
    from score
    where c_id in (
        select c_id
        from score
        where c_id = (select t_id from teacher where t_name = '张三')
    )
);

-- 方法2
select s_name
from student
where s_id not in (
    select s_id
    from score sc
             join course c
                  on sc.c_id = c.c_id
             join teacher t
                  on t.t_id = c.t_id
    where t.t_name = '张三'
);


-- 15、查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩(重点)
select s.s_id, s.s_name, avg(sc.s_score) 平均分
from score sc
         join student s
              on sc.s_id = s.s_id
group by s.s_id
having count(if(sc.s_score < 60, 1, null)) >= 2;
-- 语句执行的顺序,from   group by   avg     having

-- 16、检索"01"课程分数小于60,按分数降序排列的学生信息
select s.*
from student s,
     score sc
where s.s_id = sc.s_id
  and sc.c_id = '01'
  and sc.s_score < 60
order by sc.s_score desc;


select s.*
from score sc
         left join student s
                   on sc.s_id = s.s_id
where sc.c_id = '01'
  and sc.s_score < 60
order by sc.s_score desc;

-- 17、按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩(重重点与35一样)
-- 下面的两种方法计算平均值的方式都是不包含空值的
-- 方法一
select s_id, c.c_name, s_score, avg(s_score) over (partition by s_id) as avg
from score sc
         join course c
              on sc.c_id = c.c_id
order by avg desc;

-- 方法二
select s_id,
       max(case when c_id = '01' then s_score else 0 end) '语文',
       max(case when c_id = '02' then s_score else 0 end) '数学',
       max(case when c_id = '03' then s_score else 0 end) '英语',
       avg(s_score)                                       avg
from score
group by s_id
order by avg desc;

-- 包含空值计数的平均值
select s_id,
       max(case when c_id = '01' then s_score else 0 end) '语文',
       max(case when c_id = '02' then s_score else 0 end) '数学',
       max(case when c_id = '03' then s_score else 0 end) '英语',
       sum(s_score) / 3                                   avg
from score
group by s_id
order by avg desc;



-- 18、查询各科成绩最高分、最低分和平均分:
-- 以如下形式显示:课程ID,课程name,最高分,最低分,平均分,
-- 及格率,中等率,优良率,优秀率
-- (及格为>=60,中等为:70-80,
-- 优良为:80-90,优秀为:>=90) (超级重点)


-- 方法一
select c.c_id,
       c.c_name,
       max(sc.s_score)                                                                          max,
       min(sc.s_score)                                                                          min,
       round(avg(sc.s_score), 2)                                                                avg,
       round(count(if(sc.s_score >= 60, 1, null)) / count(sc.s_score), 2)                       '及格率',
       round(count(if((sc.s_score >= 70 and sc.s_score < 80), 1, null)) / count(sc.s_score), 2) '中等率',
       round(count(if((sc.s_score >= 80 and sc.s_score < 90), 1, null)) / count(sc.s_score), 2) '优良率',
       round(count(if(sc.s_score >= 90, 1, null)) / count(sc.s_score), 2)                       '优秀率'
from course c
         join score sc
              on c.c_id = sc.c_id
group by sc.c_id;


-- 方法二
select c.c_id,
       c.c_name,
       max(sc.s_score)                                         max,
       min(sc.s_score)                                         min,
       avg(sc.s_score)                                         avg,
       round((sum(case when sc.s_score >= 60 then 1 else 0 end) /
              sum(case when sc.s_score then 1 else 0 end)), 2) 及格率,
       round((sum(case when sc.s_score >= 70 and sc.s_score < 80 then 1 else 0 end) /
              sum(case when sc.s_score then 1 else 0 end)), 2) 中等率,
       round((sum(case when sc.s_score >= 80 and sc.s_score < 90 then 1 else 0 end) /
              sum(case when sc.s_score then 1 else 0 end)), 2) 优良率,
       round((sum(case when sc.s_score > 90 then 1 else 0 end) /
              sum(case when sc.s_score then 1 else 0 end)), 2) 优秀率
from course c
         join score sc
              on c.c_id = sc.c_id
group by sc.c_id;

-- 19、按各科成绩进行排序,并显示排名
select c_id,
       sum(s_score)                                    总分,
       dense_rank() over (order by sum(s_score) desc ) 排名
from score
group by c_id;


-- 20、查询学生的总成绩并进行排名
select s.s_id,
       s.s_name,
       sum(sc.s_score)                                   总分,
       dense_rank() over (order by sum(sc.s_score) desc) r
from score sc
         left join student s
                   on sc.s_id = s.s_id
group by sc.s_id;


-- 21、查询不同老师所教不同课程平均分从高到低显示
select t.t_id, t.t_name, c.c_name, avg(sc.s_score) avg
from course c
         left join score sc
                   on c.c_id = sc.c_id
         left join teacher t
                   on c.t_id = t.t_id
group by t.t_id
order by avg desc;


-- 22、查询所有课程的成绩第2名到第3名的学生信息及该课程成绩(重要 25类似)
select s.*, t.c_id, t.s_score, t.r
from student s
         join (
    select *, dense_rank() over (partition by c_id order by s_score desc ) r
    from score
) t
              on s.s_id = t.s_id and t.r in (2, 3);

-- 23、统计各科成绩各分数段人数:课程编号,课程名称,
-- [100-85],[85-70],[70-60],[0-60]及所占百分比
select sc.c_id,
       c.c_name,
       sum(case when sc.s_score <= 100 and sc.s_score >= 85 then 1 else 0 end) / count(*) '\[85-100\]',
       sum(case when sc.s_score < 85 and sc.s_score >= 70 then 1 else 0 end) / count(*)   '[70-85)',
       sum(case when sc.s_score < 70 and sc.s_score >= 60 then 1 else 0 end) / count(*)   '[60-70)',
       sum(case when sc.s_score < 60 and sc.s_score >= 0 then 1 else 0 end) / count(*)    '[0-60)'
from score sc
         left join course c
                   on sc.c_id = c.c_id
group by c.c_id;

select *
from score
where c_id = 02;



-- 24、查询学生平均成绩及其名次(同19题,重点)
-- 方法一
select t.*, dense_rank() over (order by avg desc ) r
from (
         select s_id, avg(s_score) avg
         from score
         group by s_id
     ) t;

-- 方法二
select s_id,
       avg(s_score)                                    avg,
       dense_rank() over (order by avg(s_score) desc ) r
from score
group by s_id;


-- 25、查询各科成绩前三名的记录(不考虑成绩并列情况)(重点 与22题类似)
select c_id,
       max(case when t.r = 1 then t.s_score else null end) '第一',
       max(case when t.r = 2 then t.s_score else null end) '第二',
       max(case when t.r = 3 then t.s_score else null end) '第三'
       -- group by 分组后的数据,每组内的的每行数据执行上面的条件语句
from (
         select *,
                row_number() over (partition by c_id order by s_score desc ) r
         from score
     ) t
group by t.c_id;

-- case 语句对 where 条件进行遍历
select *,
       row_number() over (partition by c_id order by s_score desc ) r
from score;


-- 方法二
select c_id,
       (case when t.r = 1 then t.s_score else null end) '第一',
       (case when t.r = 2 then t.s_score else null end) '第二',
       (case when t.r = 3 then t.s_score else null end) '第三'
from (
         select *,
                row_number() over (partition by c_id order by s_score desc ) r
         from score
     ) t
where t.r in (1, 2, 3);


-- 26、查询每门课程被选修的学生数
select c_id, count(s_id) 学生数
from score
group by c_id;

-- 27、查询出只有两门课程的全部学生的学号和姓名
-- 方法一
select s.s_id, s.s_name
from score sc
         left join student s
                   on sc.s_id = s.s_id
group by s.s_id
having count(sc.c_id) = 2;
-- 方法二
select s_id, s_name
from student
where s_id in (
    select s_id
    from score
    GROUP BY s_id
    HAVING COUNT(c_id) = 2);

-- 28、查询男生、女生人数
select s_sex, count(s_id) 人数
from student
group by s_sex;

-- 29、查询名字中含有"风"字的学生信息
select *
from student
where s_name like '%风%';


-- 30、查询同名同性学生名单,并统计同名人数
-- 聚合函数对 group by 的最小分组里面的数据进行聚合运算
select s1.s_name, s1.s_sex, count(*) c
from student s1
         join student s2
              on s1.s_id <> s2.s_id and
                 s1.s_name = s2.s_name and
                 s1.s_sex = s2.s_sex
group by s1.s_name, s1.s_sex;


-- 31、查询1990年出生的学生名单
select s_name
from student
where s_birth like '1990%';

/*
扩展 在不使用聚合函数的情况下
HAVING 关键字和 WHERE 关键字都可以用来过滤数据
但是 HAVING 过滤的字段必须包含在 select 字段里面

会报错
select s_name
from student
having s_birth like '1990%';
*/
-- s_birth 包含在查询字段里面 编译通过
select s_name, s_birth
from student
having s_birth like '1990%';


-- 32、查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列
select sc.c_id, avg(sc.s_score) aver
from score sc
group by sc.c_id
order by aver desc, sc.c_id asc;


-- 33、查询平均成绩大于等于85的所有学生的学号、姓名和平均成绩
select s.s_id, s.s_name, avg(sc.s_score) aver
from score sc
         left join student s
                   on sc.s_id = s.s_id
group by s.s_id
having aver >= 85;


-- 34、查询课程名称为"数学",且分数低于60的学生姓名和分数
select s.s_name, sc.s_score
from score sc
         left join student s
                   on sc.s_id = s.s_id
where sc.c_id = 02
  and sc.s_score < 60;


-- 35、查询所有学生的课程及分数情况(重点)
select s.s_id,
       s.s_name,
       max(case when sc.c_id = 01 then sc.s_score else null end) 语文,
       max(case when sc.c_id = 02 then sc.s_score else null end) 数学,
       max(case when sc.c_id = 03 then sc.s_score else null end) 英语
from student s
         left join score sc
                   on s.s_id = sc.s_id
group by s.s_id;

-- 方法二
select s.s_id,
       s.s_name,
       sum(case when c.c_name = '语文' then sc.s_score else null end) 语文,
       sum(case when c.c_name = '数学' then sc.s_score else null end) 数学,
       sum(case when c.c_name = '英语' then sc.s_score else null end) 英语
from student s
         left join score sc
                   on s.s_id = sc.s_id
         left join course c
                   on sc.c_id = c.c_id
group by sc.s_id, s.s_name;


-- 36、查询任何一门课程成绩在70分以上的姓名、课程名称和分数(重点) 注:不用group by
select s.s_name, c.c_name, sc.s_score
from student s
         join score sc
              on s.s_id = sc.s_id
         join course c
              on sc.c_id = c.c_id
where sc.s_score > 70;


-- 37、查询不及格的课程
select sc.s_id, c.c_id, c.c_name, sc.s_score
from score sc
         left join course c
                   on sc.c_id = c.c_id
where sc.s_score <= 60;


-- 38、查询课程编号为01且课程成绩在80分以上的学生的学号和姓名;
select sc.s_id, s.s_name, sc.s_score
from score sc
         left join student s
                   on sc.s_id = s.s_id
where sc.c_id = '01'
  and sc.s_score >= 80;


-- 39、求每门课程的学生人数
select c_id, count(*) 人数
from score
group by c_id;

-- 40、查询选修“张三”老师所授课程的学生中成绩最高的学生姓名及其成绩(重要top)
-- (成绩最高学生可能有n个,应该用嵌套查到最高成绩再查成绩等于最高成绩的学生信息)

-- 方法一
select s.s_id, s.s_name, sc.s_score
from student s
         join score sc
              on s.s_id = sc.s_id
         join course c
              on sc.c_id = c.c_id
         join teacher t
              on t.t_id = c.t_id
where t.t_name = '张三'
order by sc.s_score desc
limit 1;


-- 41.查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩 (重点)
select distinct s1.s_id,
                s1.c_id,
                s1.s_score
from score s1
         join score s2
              on s1.s_id = s2.s_id and
                 s1.c_id <> s2.c_id and
                 s1.s_score = s2.s_score;
-- join on 内连接:相当于连个集合,满足条件的交集

-- 42、查询每门功成绩最好的前两名
select t.c_id, t.s_score, t.r
from (
         select *, row_number() over (partition by c_id order by s_score desc ) r
         from score
     ) t
where t.r in (1, 2);



-- 43、统计每门课程的学生选修人数(超过5人的课程才统计)。
-- 要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列
select c_id, count(*) total
from score
group by c_id
having total > 5
order by total, c_id asc;


-- 44、检索至少选修两门课程的学生学号
select s_id, count(*) sel
from score
group by s_id
having sel >= 2;

-- 45、查询选修了全部课程的学生信息
select *
from student
where s_id in (
    select s_id
    from score
    group by s_id
    having count(*) = (select count(*) from course)
);
-- `HAVING` 针对分组后的结果(每组内的数据)进一步进行过滤


-- 46、查询各学生的年龄(精确到月份)
-- 方法一
select s_id, s_name, floor(datediff(now(), s_birth) / 365) 年龄
from student;
-- 方法二
select s_id, s_name, timestampdiff(year, s_birth, now()) 年龄
from student;


-- 47、查询本周过生日的学生
select *
from student
where week(date_format(now(), '%y%m%d')) = week(s_birth);

-- 查看今天是第几周
select WEEK(DATE_FORMAT(NOW(), '%Y%m%d'));

-- 48、查询下周过生日的学生
select *
from student
where WEEK(DATE_FORMAT(NOW(), '%Y%m%d')) + 1 = WEEK(s_birth);

-- 49、查询本月过生日的学生
select *
from student
where MONTH(DATE_FORMAT(NOW(), '%Y%m%d')) = MONTH(s_birth);

-- 50、查询下月过生日的学生
select *
from student
where MONTH(DATE_FORMAT(NOW(), '%Y%m%d')) + 1 = MONTH(s_birth);

参考:

  • sql语句练习50题(Mysql版)

你可能感兴趣的:(MySql)