MYSQL思考题八_Mysql 经典50题练习

这篇博客通过创建和填充学生、课程、教师和成绩表,展示了对MySQL的SQL查询能力,包括自连接、左连接、子查询、聚合函数等操作。作者通过解决一系列查询题目,加深了对SQL的理解,涵盖了学生分数对比、课程选择情况、成绩统计等多个方面。
摘要由CSDN通过智能技术生成

工作之后发现自己已经不太会写sql了,除了业务方面的学习,技术上也不能落下啊。于是打算做一遍这50题,通过实际操作来加深对sql的理解。

CREATE TABLE student (

sid varchar(10),

sname varchar(10),

sage datetime,

ssex varchar(10)

);

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

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

insert into student values('03' , '孙风' , '1990-05-20' , '男');

insert into student values('04' , '李云' , '1990-08-06' , '男');

insert into student values('05' , '周梅' , '1991-12-01' , '女');

insert into student values('06' , '吴兰' , '1992-03-01' , '女');

insert into student values('07' , '郑竹' , '1989-07-01' , '女');

insert into student values('08' , '王菊' , '1990-01-20' , '女');

CREATE TABLE course (

cid varchar(10),

cname varchar(10),

tid varchar(10)

);

insert into course values('01' , '语文' , '02');

insert into course values('02' , '数学' , '01');

insert into course values('03' , '英语' , '03');

CREATE TABLE teacher (

tid varchar(10),

tname varchar(10)

);

insert into teacher values('01' , '张三');

insert into teacher values('02' , '李四');

insert into teacher values('03' , '王五');

CREATE TABLE sc (

sid varchar(10),

cid varchar(10),

score decimal(18, 1)

);

insert into sc values('01' , '01' , 80);

insert into sc values('01' , '02' , 90);

insert into sc values('01' , '03' , 99);

insert into sc values('02' , '01' , 70);

insert into sc values('02' , '02' , 60);

insert into sc values('02' , '03' , 80);

insert into sc values('03' , '01' , 80);

insert into sc values('03' , '02' , 80);

insert into sc values('03' , '03' , 80);

insert into sc values('04' , '01' , 50);

insert into sc values('04' , '02' , 30);

insert into sc values('04' , '03' , 20);

insert into sc values('05' , '01' , 76);

insert into sc values('05' , '02' , 87);

insert into sc values('06' , '01' , 31);

insert into sc values('06' , '03' , 34);

insert into sc values('07' , '02' , 89);

insert into sc values('07' , '03' , 98);

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

keyword : 自连接(SELF JOIN)

SELECT student.*, sc1.score AS 'course-01', sc2.score AS 'course-02'

FROM student, sc sc1, sc sc2

WHERE student.sid = sc1.sid

AND sc1.sid = sc2.sid

AND sc1.cid = '01'

AND sc2.cid = '02'

AND sc1.score > sc2.score

9abffdd334fa

1.1 查询存在" 01 "课程但可能不存在" 02 "课程的情况(不存在时显示为 null )

keyword : LEFT JOIN

SELECT *

FROM

( SELECT * FROM sc WHERE sc.cid = '01' ) sc1

LEFT JOIN ( SELECT * FROM sc WHERE sc.cid = '02' ) sc2 ON sc1.sid = sc2.sid

9abffdd334fa

1.2 查询同时存在01和02课程的情况

keyword : 子查询

SELECT *

FROM

( SELECT * FROM sc WHERE sc.cid = '01' ) sc1,

( SELECT * FROM sc WHERE sc.cid = '02' ) sc2

WHERE sc1.sid = sc2.sid

9abffdd334fa

1.3 查询选择了02课程但没有01课程的情况

keyword : NOT IN

SELECT *

FROM sc

WHERE cid = '02' AND sid NOT IN ( SELECT sid FROM sc WHERE cid = '01' )

9abffdd334fa

2.查询平均成绩大于等于 60 分的同学的学生编号和学生姓名和平均成绩

keyword : GROUP BY、HAVING

--注意版本默认的model问题

SELECT sc.sid,s.sname,avg(score)

FROM student s INNER JOIN sc ON s.sid = sc.sid

GROUP BY sid

HAVING (avg(score) >= 60)

9abffdd334fa

3.查询在 SC 表存在成绩的学生信息

keyword : DISTINCT

SELECT DISTINCT student.*

FROM sc,student

WHERE sc.sid = student.sid

9abffdd334fa

4.查询所有同学的学生编号、学生姓名、选课总数、所有课程的成绩总和

keyword : INNER JOIN、COUNT()、SUM()

SELECT s.sid, s.sname,COUNT(sc.cid),SUM(sc.score)

FROM student s INNER JOIN sc ON s.sid = sc.sid

GROUP BY sc.sid

9abffdd334fa

5.查询「李」姓老师的数量

keyword : COUNT()、LIKE

SELECT COUNT(1)

FROM teacher

WHERE tname LIKE ('李%');

9abffdd334fa

6.查询学过「张三」老师授课的同学的信息

keyword : 多表连接

SELECT s.*

FROM student s INNER JOIN sc ON s.sid = sc.sid

INNER JOIN course c ON sc.cid = c.cid

INNER JOIN teacher t ON c.tid = t.tid

WHERE t.tname = '张三'

9abffdd334fa

7.查询没有学全所有课程的同学的信息

keyword : LEFT JOIN、HAVING 、COUNT()

SELECT s.*,COUNT(sc.cid)

FROM student s LEFT JOIN sc ON s.sid = sc.sid

GROUP BY sc.sid

HAVING COUNT(sc.cid) < (

SELECT COUNT(1) FROM course

)

9abffdd334fa

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

keyword : INNER JOIN、GROUP BY

SELECT s.sid,s.sname

FROM student s INNER JOIN sc on s.sid = sc.sid

WHERE sc.cid IN (

SELECT cid FROM sc WHERE sid = '01'

) AND s.sid != '01'

GROUP BY sid

9abffdd334fa

9.查询和" 01 "号的同学学习的课程完全相同的其他同学的信息

keyword : GROUP_CONCAT、ORDER BY

SELECT s.sid,sname,ssex,GROUP_CONCAT( cid ORDER BY cid )

FROM sc INNER JOIN student s ON sc.sid = s.sid

GROUP BY sc.sid

HAVING GROUP_CONCAT(cid ORDER BY cid) = (

SELECT GROUP_CONCAT(cid ORDER BY cid)

FROM sc

GROUP BY sid

HAVING sid = '01')

AND sid != '01'

-- 然而实际上去掉GROUP_CONCAT中的ORDER BY也一样能得出正确答案

-- 去掉后执行子查询会发现04号的学生得到的课程是"03,01,02"

-- 不太清楚GROUP_CONCAT的拼接规则

9abffdd334fa

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

keyword : NOT IN、LEFT JOIN

SELECT sid,sname

FROM student

WHERE sid NOT IN (

SELECT sid

FROM sc LEFT JOIN course ON sc.cid=course.cid

LEFT JOIN teacher ON course.tid=teacher.tid

WHERE tname='张三')

9abffdd334fa

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

keyword : INNER JOIN、GROUP BY、HAVING

SELECT s.sid,s.sname,COUNT(cid),AVG(score)

FROM student s INNER JOIN sc ON s.sid = sc.sid

WHERE sc.score < 60

GROUP BY sc.sid

HAVING COUNT(cid) >= 2

9abffdd334fa

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

keyword : ORDER BY

SELECT * FROM student s,sc

WHERE s.sid = sc.sid

AND sc.cid = '01'

AND sc.score < 60

ORDER BY sc.score DESC

9abffdd334fa

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

keyword : GROUP BY、ORDER BY

SELECT s.sid,s.sname,cid,score,avg_score

FROM student s INNER JOIN sc ON s.sid = sc.sid

LEFT JOIN (SELECT sid ,AVG(score) as avg_score FROM sc GROUP BY sid) t ON s.sid = t.sid

WHERE avg_score is NOT NULL

ORDER BY avg_score DESC

9abffdd334fa

14.查询各科成绩最高分、最低分和平均分,以如下形式显示:

以如下形式显示:

课程 ID,课程 name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率

及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90

要求输出课程号和选修人数,查询结果按人数降序排列,若人数相同,按课程号升序排列

keyword : GROUP BY、CASE WHEN

SELECT cid as '课程ID',cname as '课程name',COUNT(sid) as '选修人数',

MAX(score) as '最高分',MIN(score) as '最低分',AVG(score) as '平均分',

SUM(及格)/COUNT(sid) AS '及格率',

SUM(中等)/COUNT(sid) AS '中等率',

SUM(优良)/COUNT(sid) AS '优良率',

SUM(优秀)/COUNT(sid) AS '优秀率'

FROM (

SELECT sc.*,c.cname,

CASE WHEN sc.score >= 60 THEN 1 ELSE 0 END as '及格',

CASE WHEN sc.score >= 70 AND score < 80 THEN 1 ELSE 0 END as '中等',

CASE WHEN sc.score >= 80 AND score < 90 THEN 1 ELSE 0 END as '优良',

CASE WHEN sc.score >= 90 THEN 1 ELSE 0 END as '优秀'

FROM sc,course c

WHERE sc.cid = c.cid

) temp

GROUP BY temp.cid

9abffdd334fa

15.1 按各科成绩进行排序,并显示排名,Score 重复时保留名次空缺

成绩排序题参考如下:

keyword : 子查询

-- mysql5.7及以下(下文有分析) --

SELECT *, (

SELECT COUNT(DISTINCT score) + 1

FROM sc b

WHERE b.cid = a.cid AND b.score > a.score) AS 名次

FROM sc a

ORDER BY cid, 名次

-----------------------------------------------

-- mysql8.0及以上可以使用窗口函数 --

SELECT

*, DENSE_RANK() OVER(PARTITION BY cid ORDER BY score DESC) AS 'rank'

FROM

sc

分析:这应该是最直观的排名方法,对a表中每条记录,都会去b表查询比改记录分数高的数量,并且对score排重,保留名次空缺

9abffdd334fa

15.2 按各科成绩进行行排序,并显示排名, Score 重复时合并名次

keyword : 对LEFT JOIN以及 GROUP BY需要有一定的理解

先上最终答案

-- mysql5.7及以下(下文有分析) --

SELECT a.*,COUNT(b.score)+1 AS 'rank'

FROM sc AS a LEFT JOIN sc AS b ON a.cid = b.cid AND a.score < b.score

GROUP BY a.sid,a.cid

ORDER BY a.cid,COUNT(b.score)

-----------------------------------------------

-- mysql8.0及以上可以使用窗口函数 --

SELECT

*, RANK() OVER(PARTITION BY cid ORDER BY score DESC) AS 'rank'

FROM

sc

9abffdd334fa

接下来是步骤分解,为了方便理解,我们建一张简易版的test表

9abffdd334fa

简易版test表

test表自交,得到cid相等,并且a表中score小于b表score的结果集

SELECT *

FROM test AS a LEFT JOIN test AS b ON a.cid = b.cid AND a.score < b.score

9abffdd334fa

1

2.先将结果集排序一下,寻找排名的原理

SELECT *

FROM test AS a LEFT JOIN test AS b ON a.cid = b.cid AND a.score < b.score

ORDER BY a.cid,a.sid

9abffdd334fa

2

分析:

我们直观上看a的语文90分是最高,左边是null。b的语文85分是第二,左边有a-语文。c的语文60分是第三,左边有a-语文,b-语文。结合sql语句中的“test AS a LEFT JOIN test AS b ON a.cid = b.cid AND a.score < b.score”。因此test表自交并且a.score < b.score的意思就是每条a记录对应的就是分数比它高的记录。

我们列出来看看

a 语文 90 (对应为null,说明没有比它高的)

b 语文 85 a 语文 90 (对应有a-语文,说明有a-语文比它高)

c 语文 60 a 语文 90 (对应有a-语文,b-语文,说明有a-语文,b-语文比它高)

c 语文 60 b 语文 85

3.从步骤二得到规律之后就应该分组了,只要group by就能获取到对应的COUNT(b.score)作为排名,注意group by的字段选取,我们要通过sid和cid才能获取每个人每个科目的成绩,最终答案如下

SELECT a.*,COUNT(b.score)+1 AS 'rank'

FROM test AS a LEFT JOIN test AS b ON a.cid = b.cid AND a.score < b.score

GROUP BY a.sid,a.cid /*不懂的话可以拿步骤2的数据group by a.sid,a.cid一次*/

ORDER BY a.cid,COUNT(b.score)

9abffdd334fa

3

16.1 查询学生的总成绩,并进行排名,总分重复时保留名次空缺

keyword : 用户变量

SELECT t1.*,

@curRank := IF (@perScore = sum_score,@curRank, @curRank+1) as 'rank',

@perScore := sum_score

FROM

(SELECT sid,SUM(score) AS sum_score FROM sc GROUP BY sid) AS t1 ,

(SELECT @curRank:=0,@perScore := NULL) AS t2

ORDER BY sum_score DESC

分析:整体还是比较好理解的,创建两个用户变量@curRank (当前排名)和@perScore(前一个学生的总分),在遍历时比较当前的总分和前一个的总分是否相等,相等的话@curRank 不变,否则@curRank +1,每次遍历都需要更新一次@perScore

9abffdd334fa

为了模拟同分的情况!sid=3,cid=1的分数从80改成50

16.2 查询学生的总成绩,并进行排名,总分重复时不保留名次空缺

keyword : 用户变量

SELECT

t1.*, @curRank := IF(sum_score=@preScore, @curRank, @totalRank) AS 'rank',

@preScore := sum_score,

@totalRank := @totalRank+1

FROM

(SELECT sid,SUM(score) AS sum_score FROM sc GROUP BY sid) AS t1,

(SELECT @curRank:=1, @totalRank:=1, @preScore:=NULL) t2

ORDER BY sum_score DESC

分析:和上一题基本一致,再加多了一个用户变量@totalRank,每次遍历都+1,然后当前总分和上一个学生总分相同时,排名是@curRank不变,否则

更新@curRank为@totalRank

9abffdd334fa

为了模拟同分的情况,sid=3,cid=1的分数我从80改成50

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

keyword : CASE WHEN

SELECT

sc.cid AS 课程编号,

cname AS 课程名称,

SUM( CASE WHEN score >= 0 AND score <= 60 THEN 1 ELSE 0 END ) AS '[60-0]',

SUM( CASE WHEN score >= 0 AND score <= 60 THEN 1 ELSE 0 END ) / COUNT( sid ) AS '[60-0]百分比',

SUM( CASE WHEN score >= 60 AND score <= 70 THEN 1 ELSE 0 END ) AS '[70-60]',

SUM( CASE WHEN score >= 60 AND score <= 70 THEN 1 ELSE 0 END ) / COUNT( sid ) AS '[70-60]百分比',

SUM( CASE WHEN score >= 70 AND score <= 85 THEN 1 ELSE 0 END ) AS '[85-70]',

SUM( CASE WHEN score >= 70 AND score <= 85 THEN 1 ELSE 0 END ) / COUNT( sid ) AS '[85-70]百分比',

SUM( CASE WHEN score >= 85 AND score <= 100 THEN 1 ELSE 0 END ) AS '[100-85]',

SUM( CASE WHEN score >= 85 AND score <= 100 THEN 1 ELSE 0 END ) / COUNT( sid ) AS '[100-85]百分比'

FROM

sc JOIN course ON sc.cid = course.cid

GROUP BY sc.cid,cname

9abffdd334fa

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

keyword :HAVING

SELECT *, (

SELECT COUNT(score) + 1

FROM sc b

WHERE b.cid = a.cid AND b.score > a.score) AS ranking

FROM sc a

HAVING ranking <= 3

ORDER BY cid, ranking

9abffdd334fa

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

keyword : GROUP BY

SELECT cid,COUNT(sid) AS num

FROM sc

GROUP BY cid

9abffdd334fa

查询出只选修两门课程的学生学号和姓名

keyword : INNER JOIN、HAVING

SELECT sc.sid,sname

FROM sc INNER JOIN student ON sc.sid = student.sid

GROUP BY sc.sid

HAVING COUNT(cid) = 2

9abffdd334fa

查询男生、女生人数

keyword : GROUP BY

SELECT ssex,COUNT(sid) as num

FROM student

GROUP BY ssex

9abffdd334fa

查询名字中含有「风」字的学生信息

keyword : LIKE

SELECT *

FROM student

WHERE sname LIKE '%风%'

9abffdd334fa

查询同名同性学生名单,并统计同名人数

keyword : LEFT JOIN、GROUP BY

SELECT a.sname,a.ssex,COUNT(1) AS 人数

FROM student a LEFT JOIN student b ON a.sname=b.sname

WHERE a.ssex=b.ssex AND a.sid!=b.sid

GROUP BY a.sname,a.ssex

9abffdd334fa

查询 1990 年出生的学生名单

keyword : 时间函数

SELECT *

FROM student

WHERE YEAR (sage) = '1990'

9abffdd334fa

查询每门课程的平均成绩,结果按平均成绩降序排列,平均成绩相同时,按课程编号升序排列

keyword : ORDER BY

SELECT cid,AVG(score) as avgScore

FROM sc

GROUP BY cid

ORDER BY avgScore DESC,cid ASC

9abffdd334fa

查询平均成绩大于等于 85 的所有学生的学号、姓名和平均成绩

keyword : AVG()、HAVING

SELECT s.sid,sname,AVG(score) as avgScore

FROM sc,student s

WHERE sc.sid = s.sid

GROUP BY sid

HAVING avgScore > 85

9abffdd334fa

查询课程名称为「数学」,且分数低于 60 的学生姓名和分数

keyword : INNER JOIN

SELECT s.sname,sc.score

FROM student AS s INNER JOIN sc ON s.sid = sc.sid

WHERE sc.score < 60

AND sc.cid IN (SELECT cid FROM course WHERE cname = '数学')

9abffdd334fa

查询所有学生的课程及分数情况(存在学生没成绩,没选课的情况)

keyword : INNER JOIN

SELECT sname,cid,score

FROM sc INNER JOIN student AS s ON sc.sid = s.sid

9abffdd334fa

查询任何一门课程成绩在 70 分以上的姓名、课程名称和分数

keyword : INNER JOIN

SELECT sname,cid,score

FROM sc INNER JOIN student AS s ON sc.sid = s.sid

WHERE score > 70

9abffdd334fa

查询不及格的课程

keyword : DISTINCT

SELECT DISTINCT sc.cid

FROM sc INNER JOIN student AS s ON sc.sid = s.sid

WHERE score < 60

9abffdd334fa

31.查询课程编号为 01 且课程成绩在 80 分以上的学生的学号和姓名

keyword : INNER JOIN

SELECT s.sid,sname

FROM sc INNER JOIN student AS s ON sc.sid = s.sid

WHERE sc.cid = '01' AND score >= 80

9abffdd334fa

32.求每门课程的学生人数

keyword :GROUP BY、COUNT()

SELECT cid,COUNT(sid) AS num

FROM sc

GROUP BY cid

9abffdd334fa

33.成绩不重复的情况下,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩

keyword :INNER JOIN、MAX()

SELECT *,MAX(score)

FROM student s INNER JOIN sc ON s.sid =sc.sid

WHERE sc.cid IN (

SELECT cid

FROM course c,teacher t

WHERE c.tid = t.tid AND t.tname = '张三'

)

9abffdd334fa

34.成绩重复的情况下,查询选修「张三」老师所授课程的学生中,成绩最高的学生信息及其成绩

keyword :多重JOIN、MAX()

SELECT student.*, sc.cid, score

FROM student

INNER JOIN sc ON student.sid = sc.sid

LEFT JOIN course ON sc.cid = course.cid

LEFT JOIN teacher ON course.tid = teacher.tid

WHERE tname = '张三'

AND score = (

SELECT MAX(score)

FROM sc

INNER JOIN course ON sc.cid = course.cid

LEFT JOIN teacher ON course.tid = teacher.tid

WHERE tname = '张三'

)

9abffdd334fa

为了模拟同分的情况,sid=7,cid=2的分数我从89改成90

35.查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩

keyword :DISTINCT

SELECT DISTINCT a.*

FROM sc AS a INNER JOIN sc AS b

WHERE a.score = b.score AND a.cid != b.cid

9abffdd334fa

查询每门成绩最好的前两名

keyword :窗口函数

SELECT *

FROM

( SELECT *, RANK ( ) OVER ( PARTITION BY cid ORDER BY score DESC ) AS ranking FROM sc ) t

WHERE ranking <= 2

9abffdd334fa

37.统计每门课程的学生选修人数(超过 5 人的课程才统计)

keyword :GROUP BY、HAVING

SELECT cid,COUNT(sid)

FROM sc

GROUP BY cid

HAVING COUNT(sid) > 5

9abffdd334fa

38.检索至少选修两门课程的学生学号

*keyword :GROUP BY、HAVING *

SELECT sid,COUNT( cid )

FROM sc

GROUP BY sid

HAVING COUNT( cid ) >= 2

9abffdd334fa

39.查询选修了全部课程的学生信息

keyword:INNER JOIN

SELECT s.*

FROM sc INNER JOIN student AS s ON sc.sid = s.sid

WHERE cid = ( SELECT COUNT( 1 ) FROM course );

9abffdd334fa

40.查询各学生的年龄,只按年份来算

keyword:YEAR()

SELECT sname,YEAR(NOW()) - YEAR(sage) AS 年龄

FROM student

9abffdd334fa

按照出生日期来算,当前月日 < 出生年月的月日则,年龄减一

keyword:CASE WHEN、YEAR()

SELECT sname,

CASE WHEN ( DATE_FORMAT( NOW( ), '%m-%d' ) - DATE_FORMAT( sage, '%m-%d' ) ) < 0

THEN YEAR ( NOW( ) ) - YEAR ( sage ) - 1

ELSE YEAR ( NOW( ) ) - YEAR ( sage )

END AS 年龄

FROM student

9abffdd334fa

42.查询本周过生日的学生

keyword:WEEK()

SELECT sname

FROM student

WHERE WEEK(sage) = WEEK(NOW())

9abffdd334fa

43.查询下周过生日的学生

keyword:WEEK()

SELECT sname

FROM student

WHERE WEEK(sage) = WEEK(NOW()) + 1

9abffdd334fa

44.查询本月过生日的学生

keyword:MONTH ()

SELECT sname,sage

FROM student

WHERE MONTH (sage) = MONTH (NOW())

9abffdd334fa

45.查询下月过生日的学生

keyword:MONTH ()

SELECT sname,sage

FROM student

WHERE MONTH (sage) = MONTH (NOW()) + 1

9abffdd334fa

结语:终于做完了(有一些题目是有小题的,总体还是有50题的),如果有错误或者更好的方法,欢迎指出

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值