經典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);

參考:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章