MySQL面试50题

1 1到5

-- 1 查询课程编号为‘01’的课程比‘02’的课程成绩高的所有学生的学号(重点)

-- 整体的机构是先把三个表合并起来,再用where语句进行筛选,这里的as都可以省略,
-- select 
-- a.s_id as "sno", 
-- a.s_score as "01", 
-- b.s_score as "02",
-- c.s_name 
-- from 
-- (select s_id,c_id,s_score from score where c_id = '01') as a
-- inner join 
-- (select s_id,c_id,s_score from score where c_id = '02')as b 
-- on a.s_id = b.s_id 
-- inner join 
-- student as c on c.s_id = a.s_id 
-- where a.s_score > b.s_score


-- 2 查询平均成绩大于60分的学生学号和平均成绩

-- -- 注意select后面的列尽量只选在group by后面出现的字段或使用统计函数统计过的字段,不要添加其它的列,因为没有意义
-- select s_id, avg(s_score), c_id
-- from score 
-- group by s_id having avg(s_score)>60


-- 3 查询所有同学的学号、姓名、选课总数、总成绩
-- 使用的表:student,score

-- 由于有的学生可能没有成绩,所以这里用左连接,
-- select后面尽量只出现groupby后面的字段和统计字段,所以把名字列也加到groupby后面
-- 为了避免求和列出现null值,所以判断为空值时赋值为0
-- select a.s_id,a.s_name, count(b.s_id),
-- sum(case when b.s_score is null then 0 else b.s_score end)
-- from student as a left join score as b on a.s_id = b.s_id
-- group by s_id,a.s_name


-- 4 查询姓猴老师的个数
-- 使用的表:teacher

-- %代表任意字符,如果姓氏有重复可以用count(distinct t_name)来去重后再统计
-- select count(t_name)
-- from teacher 
-- where t_name like '猴%'


-- 5 查询没学过张三老师课的学生的学号,姓名(重点)

-- 注意这里必须是用not in方法来解决,否则是错误的,即先把选过张三老师课程的学号找到,再取反
-- select s_id, s_name from student
-- where s_id not in (
-- select s_id from score 
-- where c_id = (
-- select c_id from course 
-- where t_id = 
-- (select t_id from teacher where t_name = '张三')
-- )
-- )

-- 错误写法,这样选出的是其它老师上的课
-- select * from score where c_id != '02'

-- 先把三个表合并,再进行选择
-- select s_id, s_name from student
-- where s_id not in 
-- (
-- select s.s_id from score as s
-- inner join course as c on s.c_id = c.c_id
-- inner join teacher as t on c.t_id = t.t_id
-- where t.t_name = '张三'
-- )
View Code

2 6到10

-- 6 查询学过’张三‘老师所教的所有课的同学的学号,姓名(重点)

-- 自己的代码,可能有问题,先把选张三老师的课的学号选出来,再从student表中选对应的学号
-- select s_id,s_name from student as st
-- where st.s_id in 
-- (
-- select s.s_id from score as s 
-- inner join course as c on s.c_id = c.c_id 
-- inner join teacher as t on t.t_id = c.t_id 
-- where t.t_name = '张三'
-- )

-- 把四个表直接合并,再选择
-- select st.s_id, st.s_name
-- from student as st
-- inner join score as s on st.s_id = s.s_id 
-- inner join course as c on c.c_id = s.c_id 
-- inner join teacher as t on t.t_id = c.t_id 
-- where t.t_name = '张三'
-- order by st.s_id
-- 


-- 7 查询学过编号为‘01’的课程且学过编号为‘02’的课程的学生的学号,姓名(重点) 

-- 先利用子查询建立两个表,再用inner join表连接来筛选出都学过的学号,再对student表进行筛选
-- select s_id, s_name from student
-- where s_id in
-- (
-- select a.s_id from
-- (select s_id from score where c_id = '01') as a
-- inner join 
-- (select s_id from score where c_id = '02') as b
-- on a.s_id = b.s_id 
-- )
-- NULL求长度后结果是null  select LENGTH(null)


-- 8 查询课程编号为‘02’的总成绩(不重点)

-- select sum(s_score),avg(s_score),count(distinct s_id) from score 
-- where c_id = '02'
-- -- 利用groupby可以对不同课程做统计
-- select c_id,sum(s_score),avg(s_score),count(distinct s_id) from score 
-- group by c_id
-- -- 或者利用having
-- select c_id,sum(s_score),avg(s_score),count(distinct s_id) from score 
-- group by c_id having c_id = '02'


-- 9 查询所有课程成绩小于60分的学生的学号,姓名

-- 先计算每个同学小于60分的课程的个数,再计算每个同学所学的课程数,然后利用子查询把两个表按学号连接起来
-- 再对表进行筛选,最后和student表进行联结
-- 注意1>select后面是最后要展示的列  2>where要再join的后面不能先筛选了再进行join
-- select a.s_id, st.s_name
-- from 
-- (
-- select s_id, count(c_id) as cnt from score 
-- where s_score < 60
-- group by s_id
-- ) as a
-- inner join 
-- (
-- select s_id, count(c_id) as cnt from score 
-- group by s_id
-- ) as b 
-- on a.s_id = b.s_id 
-- inner join student as st 
-- on a.s_id = st.s_id
-- where a.cnt = b.cnt


-- 10 查询没有学全所有课的学生的学号、姓名(重点)

-- 利用group by进行聚合筛选
-- select只能跟列名或者*,不能直接选择表名,选表名会报错 
-- 利用in方法的时候后面只能选一个列,选多个列的时候无法筛选,会报错
-- select s_id, s_name from student 
-- where s_id in 
-- (
-- select s_id from score
-- group by s_id having count(c_id) < (select count(distinct c_id) from course)
-- )

-- 上面方法有缺陷,因为可能有的学生没有成绩,所以在score表中不会出现,改进后的方法
-- 把student表和score表合并后再筛选
-- 注意这里之所以要用left join是因为student中的学生多,是一对多,
-- select st.s_id, st.s_name from student as st 
-- left join score as sc 
-- on st.s_id = sc.s_id 
-- group by st.s_id having count(distinct sc.c_id) < (select count(distinct c_id) from course)
View Code

3 11到15

-- 11 查询至少有一门课与学号为“01”的学生所学课程相同的学生的学号和姓名(重点)

-- 用in效率较低,适合小数据 注意and在括号外用
-- select * from student 
-- where s_id in 
-- (select distinct s_id from score
-- where c_id in  
-- (select c_id from score 
-- where s_id = '01') 
-- and s_id != '01')
-- 当表较大的时候,用inner join ... on 比用in效率高
-- select a.s_id,a.s_name from student as a
-- inner join 
-- (
-- (select distinct s_id from score
-- where c_id in  
-- (select c_id from score 
-- where s_id = '01') 
-- and s_id != '01')
-- ) as b on a.s_id = b.s_id


-- 12 查询和“01”号同学所学课程完全相同的其他同学的学号(重点)

-- 思路:01号同学选了(01 02 03)三门课程,先选出所有和01号同学学习课程数目相同的学生,
--       再把其中选了其它课程的同学删除,或者反过来理解也行,
-- select * from student 
-- where s_id in 
-- (
-- select s_id from score 
-- where s_id != '01'
-- group by s_id having count(distinct c_id) = (select count(distinct c_id) from score where s_id = '01')
-- )
-- and s_id not in
-- (
-- select distinct s_id from score 
-- where c_id not in 
-- (select c_id from score where s_id = '01')
-- )

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

-- 先把满足条件的学号筛选处来,再对合并后的表格进行筛选和groupby
-- select st.s_id, st.s_name, avg(s_score) from student as st 
-- inner join score as sc on st.s_id = sc.s_id
-- where st.s_id in 
-- (
-- select s_id from score 
-- where s_score < 60
-- group by s_id having count(distinct c_id) >= 2
-- )
-- group by s_id, s_name
View Code

4 16到20

-- 16 检索"01"课程分数小于60,按分数降序排列的学生信息(和34题重复,不重点)

-- 考察了order by ,where实际上是用于筛选条件的
-- select st.*,sc.c_id,sc.s_score from student as st 
-- inner join score as sc on st.s_id = sc.s_id
-- where sc.c_id = '01' and sc.s_score < 60
-- order by sc.s_score desc


-- 17 按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩(重重点与35一样)

-- 自己的代码,注意inner join后面必须要有on,否则会出错,代码有问题,没有课程列
-- select k.s_id, k.s_name, avg(s_score) from 
-- (
-- select st.*, sc.s_score from student as st
-- inner join score as sc on st.s_id = sc.s_id
-- ) as k
-- group by s_id  
-- order by avg(s_score) desc

-- 这个代码有问题,因为s_score列并没有意义,也不满足题目要求
-- select sc1.s_id, sc1.s_score, avg_s_score  from score as sc1 
-- inner join 
-- (
-- select s_id, avg(s_score) as avg_s_score from score 
-- group by s_id
-- ) as sc2 on sc1.s_id = sc2.s_id 
-- order by avg_s_score desc

-- 这个写法非常巧妙,这里之所以要用统计函数max是因为group by前最好只出现group by使用过的列或统计函数
-- 这里的目的并不是为了求max,而是因为group by用法的原因,
-- select s_id '学号',
-- max(case when c_id = '01' then s_score else null end) '语文',
-- max(case when c_id = '02' then s_score else null end) '数学',
-- max(case when c_id = '03' then s_score else null end) '英语',
-- avg(s_score) '平均成绩'
-- from score 
-- group by s_id
-- order by avg(s_score) desc


-- 18 查询各科成绩最高分、最低分和平均分:以如下形式显示:课程ID,课程name,
-- 最高分,最低分,平均分,及格率,中等率,优良率,优秀率

-- 这个题主要考察了group by后聚合函数case when的用法
-- select 
-- sc.c_id
-- ,c.c_name
-- ,max(s_score)
-- ,min(s_score)
-- ,avg(s_score)
-- ,sum(case when sc.s_score>=60 then 1 else 0 end)/count(s_id) '及格率'
-- ,sum(case when sc.s_score>=70 and sc.s_score<=80 then 1 else 0 end)/count(s_id) '中等率'
-- ,sum(case when sc.s_score>=80 and sc.s_score<=90 then 1 else 0 end)/count(s_id) '优良率'
-- ,sum(case when sc.s_score>=90 and sc.s_score<=100 then 1 else 0 end)/count(s_id) '优秀率'
-- from score as sc 
-- inner join course as c on sc.c_id = c.c_id 
-- group by c_id 


-- 19 按各科成绩进行排序,并显示排名(重点row_number) row_number()over (order by 列)

-- rank()       跳跃排序,即如果第二名和第三名分数一样,则二三都是2,第四名为4
-- dense_rank() 连续排序,二三同上,第四名为3,即名次连续
-- row_number() 无重复值排序,直接为1 2 3 4,不分是否分数相等
-- select s_id, c_id, s_score, row_number()
-- over(order by s_score desc) from score


-- 20 查询学生的总成绩并进行排名(不重点)

-- 有问题,为什么列名sum(s_score)在外面无法选择
select * 
from student as st 
inner join 
(
select s_id, sum(s_score) from score
group by s_id
order by sum(s_score) desc
) as sc on st.s_id = sc.s_id
View Code

5 21到30

-- 21 查询不同老师所教不同课程平均分从高到低显示(不重点)

-- 自己的代码,和视频里不一样,把表合并为一个大表后,前面可以自由的选择列
-- select t.t_name, c.c_name, avg(s_score) from teacher as t
-- left join course as c on c.t_id = t.t_id
-- left join score as sc on sc.c_id = c.c_id 
-- group by t.t_name, c.c_name
-- order by avg(s_score) desc


-- 22 查询所有课程的成绩第2名到第3名的学生信息及该课程成绩(重要 25类似)

-- 考察了窗口函数,先做一个子查询,再用where对m列进行筛选,这里的窗口函数其实可以用rank,因为有相同分数的情况
-- select * 
-- from 
-- (select st.s_id, st.s_name, c_id, s_score, 
-- row_number() over (partition by c_id order by s_score desc) m
-- from score as sc 
-- inner join student as st 
-- on sc.s_id = st.s_id) as tab 
-- where m in (2,3)


-- 23 使用分段[100-85],[85-70],[70-60],[<60]来统计各科成绩,分别统计各分数段人数:课程ID和课程名称(重点和18题类似)

-- 这个题主要考察了统计函数sum 和 count的不同用法,注意count是对非空值进行统计
-- 这里的内连接和视频不同,导致结果不同
-- select c.c_id
-- , c.c_name
-- , sum(case when sc.s_score<=100 and sc.s_score>=85 then 1 else 0 end) '[100-85]'
-- , count(case when sc.s_score<=85 and sc.s_score>=70 then 3 else null end) '[85-70]'
-- , count(case when sc.s_score<=70 and sc.s_score>=60 then 3 else null end) '[70-60]'
-- , count(case when sc.s_score<60 then 3 else null end) '[<60]'
-- from course as c 
-- inner join score as sc 
-- on c.c_id = sc.c_id 
-- group by c.c_id, c.c_name


-- 24 查询学生平均成绩及其名次(同19题,重点)

-- 注意窗口函数不用partition by时,就是对整体的数据进行排序
-- select s_id
-- , avg(s_score)
-- , row_number() over (order by avg(s_score) desc)
-- from score 
-- group by s_id


-- 26 查询每门课程被选修的学生数(不重点)

-- group by后面的列尽量都在select中
-- select c.c_id, c.c_name, count(distinct sc.s_id) '学生数' from score as sc
-- inner join course as c 
-- on sc.c_id = c.c_id
-- group by c.c_id, c.c_name 


-- 27 查询出只有两门课程的全部学生的学号和姓名(不重点)

-- 主要考察如何作筛选
-- 利用子查询,先把s_id找出来,再筛选
-- select st.s_id, st.s_name from student as st
-- where s_id in 
-- (
-- select s_id from score 
-- group by s_id having count(distinct c_id)=2
-- )

-- 先把两个表联结后再group by利用having进行筛选
-- select st.s_id, st.s_name from score as sc 
-- inner join student as st 
-- on sc.s_id = st.s_id 
-- group by s_id having count(distinct c_id)=2


-- 28 查询男生、女生人数(不重点)

-- 这道题把group by去掉也行
-- 利用count实现
-- select s_sex
-- , count(s_sex) '人数'
-- from student
-- group by s_sex 
-- 利用sum实现
-- select 
-- sum(case when s_sex='男' then 1 else 0 end) '男',
-- sum(case when s_sex='女' then 1 else 0 end) '女'
-- from student

-- 29 查询名字中含有"风"字的学生信息(不重点)

-- 考察like的用法,%风是以风开头,风%是以风结尾
-- select * from student where s_name like '%风%'
View Code

6 31到40

-- 31 查询1990年出生的学生名单(重点year)

-- 考察时间函数的用法
-- select * from student where year(s_birth) = 1990
-- select month('190101')
-- 时间格式如下的可以直接转换
-- YYYYMM-DD   YYYYMMDD   YYYY/MM//DD  YYMMDD


-- 32 查询平均成绩大于等于85的所有学生的学号、姓名和平均成绩(不重要)

-- 注意group by后面用having可以起筛选作用,这道题也可以先对score表求平均后,再用子查询和表student做联结
-- select st.s_id, st.s_name, avg(s_score) as '平均成绩' from student as st 
-- inner join score as sc 
-- on st.s_id = sc.s_id 
-- group by st.s_id, st.s_name having avg(s_score)>85


-- 33 查询每门课程的平均成绩,结果按平均成绩升序排序,平均成绩相同时,按课程号降序排列(不重要)

-- 考察order by同时排序两列的用法
-- select sc.c_id, c.c_name, avg(s_score) from score as sc 
-- inner join course as c on sc.c_id = c.c_id 
-- group by c_id 
-- order by avg(s_score) asc, c_id desc 


-- 34 查询课程名称为"数学",且分数低于60的学生姓名和分数(不重点)

-- 考察了利用where进行条件筛选,写程序时先筛选完再inner join联结student表来添加名字列
-- select st.s_id, st.s_name, sc.s_score from score as sc 
-- inner join course as c on sc.c_id = c.c_id
-- inner join student as st on sc.s_id = st.s_id
-- where c.c_name = '数学' and sc.s_score<60


-- 35 查询所有学生的课程及分数情况(重点)

-- 这个题主要考察了利用group by把一列按条件拆成多列的写法,这里case when的用法是groupby后的每个c.c_name
-- 都会与语文进行比较,所以除了语文,其余都是空值,在空值和语文分数中取最大值,结果就是语文分数,
-- select st.s_id, st.s_name,
-- max( case when c.c_name='语文' then sc.s_score else null end) '语文',
-- max( case when c.c_name='数学' then sc.s_score else null end) '数学',
-- max( case when c.c_name='英语' then sc.s_score else null end) '英语'
-- from score as sc 
-- inner join student as st on sc.s_id = st.s_id
-- inner join course as c on sc.c_id = c.c_id 
-- group by st.s_id, st.s_name


-- 36 查询课程成绩在70分以上的姓名、课程名称和分数(重点)

-- 由于是score对student course表都是多对一,所以可以用inner join
-- select st.s_name, c.c_name, sc.s_score  from score as sc 
-- inner join student as st on sc.s_id = st.s_id 
-- inner join course as c on c.c_id = sc.c_id 
-- where sc.s_score > 70

-- 37查询不及格的课程并按课程号从大到小排列(不重点)

-- select st.s_id, st.s_name, c.c_name, c.c_id, sc.s_score
-- from score as sc 
-- inner join course as c on sc.c_id = c.c_id
-- inner join student as st on sc.s_id = st.s_id
-- where sc.s_score < 60
-- order by c.c_id desc

-- 38 查询课程编号为03且课程成绩在80分以上的学生的学号和姓名(不重要)

-- select st.s_id, st.s_name, sc.s_score, c.c_name from score as sc 
-- inner join student as st on st.s_id = sc.s_id 
-- inner join course as c on c.c_id = sc.c_id
-- where sc.c_id='03' and sc.s_score>80 

-- 39 求每门课程的学生人数(不重要)

-- select c.c_id, c.c_name, count(distinct c.c_id) as '人数' from score as sc 
-- inner join course as c on sc.c_id = c.c_id 
-- group by c.c_id, c.c_name

-- 40查询选修“张三”老师所授课程的学生中成绩最高的学生姓名及其成绩(重要top)

-- 考察了limit的用法,用于排序后取某些行,左闭右开,
-- select st.s_id, st.s_name, sc.s_score from score as sc 
-- inner join course as c on c.c_id=sc.c_id
-- inner join teacher as t on t.t_id=c.t_id 
-- inner join student as st on st.s_id=sc.s_id
-- where t.t_name='张三' 
-- order by sc.s_score desc limit 0,1
View Code

7 41到50

-- 41 查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩 (重点)

-- 多个子查询嵌套,先把只选一个课程的去掉,再两次group by进行筛选
-- select s_id from 
-- (
-- select b.s_id, b.s_score from score as b 
-- inner join 
-- (
-- select s_id from score 
-- group by s_id having count(distinct c_id)>1
-- ) as c on b.s_id=c.s_id
-- group by b.s_id, b.s_score
-- ) as a 
-- group by s_id having count(s_id)=1


-- 43 统计每门课程的学生选修人数(超过5人的课程才统计)。要求输出课程号和选修人数,
--    查询结果按人数降序排列,若人数相同,按课程号升序排列(不重要)

-- where后面不能跟聚合函数
-- select sc.c_id, count(distinct s_id) from score as sc 
-- group by sc.c_id having count(s_id)>5
-- order by count(s_id) desc, c_id asc

-- 44、检索至少选修两门课程的学生学号(不重要)

-- 使用count函数的时候要判断是否需要用distinct
-- select s_id, count(distinct c_id) from score 
-- group by s_id having count(distinct c_id)>=2

-- 45 查询选修了全部课程的学生信息(重点划红线地方)

-- 要学会这里计算course表时的子查询,直接用count(course.c_id)会报错,因为course表没有被选中
-- select s_id from score as sc 
-- group by s_id having count(distinct c_id)=
-- (select count(distinct c_id) from course)


-- 46 查询各学生的年龄(精确到月份)
-- floor()函数用于向下取整, datediff(date1, data2) 第一个参数减第二个差值是天数,now()获取当前时间
-- select s_id, s_birth, floor(datediff(now(), s_birth)/365) as '年龄' from student

-- 47 查询没学过“张三”老师讲授的任一门课程的学生姓名

-- 必须用取反的方法not in
-- select * from student 
-- where s_id not in 
-- (
-- select sc.s_id from score as sc 
-- inner join course as c on sc.c_id=c.c_id
-- inner join teacher as t on t.t_id=c.t_id 
-- where t.t_name='张三'
-- )

-- 48 查询下周过生日的同学

-- 先把年份做字符串替换,替换成今年的,再做判断,年末不够严谨
-- select * from student
-- where week(concat('2020-', substring(s_birth, 6, 5)), 1) = week('2019-05-15', 1)+1

-- 49 查询本月过生日的人
-- select * from student 
-- where month(s_birth)=month(now())

-- 50 查询下月过生日的人
-- 为防止出现13直接对12取整
-- select * from student 
-- where month(s_birth)=mod(month(now())+1, 12)
View Code
原文地址:https://www.cnblogs.com/xxswkl/p/12843681.html