SQL面试必会50题
Posted Miraclo_acc
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL面试必会50题相关的知识,希望对你有一定的参考价值。
引用:
视频讲解:https://www.bilibili.com/video/BV1q4411G7Lw/
SQL面试必会50题: https://zhuanlan.zhihu.com/p/43289968
图解SQL面试题:经典50题:https://zhuanlan.zhihu.com/p/38354000
其中重点为:1/2/5/6/7/10/11/12/13/15/17/18/19/22/23/25/31/35/36/40/41/42/45/46 共16题
超级重点 18和23、 22和25 、 41、46
几个重要函数
①按照某列排序:row_number()over ([partition by 字段(按照字段分组)] order by 列)【19】
②行转列、按值分段:CASE WHEN c_id = ‘01’ THEN s_score ELSE null END【17、35】
③日期函数如年月日:
year()
、week()
、month()
【31】④查询年龄、查询生日:
datediff(data1, data2)
,返回data1-data2的天数【46,47,48,50】
文章目录
- 图解SQL面试题:经典50题
- 1. 查询课程编号为“01”的课程比“02”的课程成绩高的所有学生的学号(重点)
- 5. 查询没学过“张三”老师课的学生的学号、姓名(重点)
- 6. 查询学过“张三”老师所教的所有课的同学的学号、姓名(重点)
- 7. 查询学过编号为“01”的课程并且也学过编号为“02”的课程的学生的学号、姓名(重点)
- 10. 查询没有学全所有课的学生的学号、姓名(重点)
- 11. 查询至少有一门课与学号为“01”的学生所学课程相同的学生的学号和姓名(重点)
- 12. 查询和“01”号同学所学课程完全相同的其他同学的学号(重点)
- 13. 查询没学过"张三"老师讲授的任一门课程的学生姓名(重点,同5)
- 15. 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩(重点)
- 17、按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩(重重点与35一样)
- 18.查询各科成绩最高分、最低分和平均分:以如下形式显示:课程ID,课程name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率 (超级重点)
- 19. 按各科成绩进行排序,并显示排名(重点)
- 25. 查询各科成绩前三名的记录(不考虑成绩并列情况)(重点 与22题类似)
- 41. 查询不同课程成绩相同的学生的学生编号、课程编号、学生成绩 (重点)
图解SQL面试题:经典50题
ps:这些题考察SQL的编写能力,对于这类型的题目,需要你先把4张表之间的关联关系搞清楚了,最好的办法是自己在草稿纸上画出关联图,然后再编写对应的SQL语句就比较容易了。下图是我画的这4张表的关系图,可以看出它们之间是通过哪些外键关联起来的。
已知有如下4张表:
学生表:
Student(s_id,s_name,s_birth,s_sex) –学生编号,学生姓名, 出生年月,学生性别
成绩表:
Score(s_id,c_id,s_score) –学生编号,课程编号,分数
课程表:
Course(c_id,c_name,t_id) –课程编号, 课程名称, 教师编号
教师表:
Teacher(t_id,t_name) –教师编号,教师姓名
测试数据:
-- 建表
-- 学生表
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`)
);
-- 课程表
CREATE TABLE `Course`(
`c_id` VARCHAR(20),
`c_name` VARCHAR(20) NOT NULL DEFAULT '',
`t_id` VARCHAR(20) NOT NULL,
PRIMARY KEY(`c_id`)
);
-- 教师表
CREATE TABLE `Teacher`(
`t_id` VARCHAR(20),
`t_name` VARCHAR(20) NOT NULL DEFAULT '',
PRIMARY KEY(`t_id`)
);
-- 成绩表
CREATE TABLE `Score`(
`s_id` VARCHAR(20),
`c_id` VARCHAR(20),
`s_score` INT(3),
PRIMARY KEY(`s_id`,`c_id`)
);
-- 插入学生表测试数据
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' , '女');
-- 课程表测试数据
insert into Course values('01' , '语文' , '02');
insert into Course values('02' , '数学' , '01');
insert into Course values('03' , '英语' , '03');
-- 教师表测试数据
insert into Teacher values('01' , '张三');
insert into Teacher values('02' , '李四');
insert into Teacher values('03' , '王五');
-- 成绩表测试数据
insert into Score values('01' , '01' , 80);
insert into Score values('01' , '02' , 90);
insert into Score values('01' , '03' , 99);
insert into Score values('02' , '01' , 70);
insert into Score values('02' , '02' , 60);
insert into Score values('02' , '03' , 80);
insert into Score values('03' , '01' , 80);
insert into Score values('03' , '02' , 80);
insert into Score values('03' , '03' , 80);
insert into Score values('04' , '01' , 50);
insert into Score values('04' , '02' , 30);
insert into Score values('04' , '03' , 20);
insert into Score values('05' , '01' , 76);
insert into Score values('05' , '02' , 87);
insert into Score values('06' , '01' , 31);
insert into Score values('06' , '03' , 34);
insert into Score values('07' , '02' , 89);
insert into Score values('07' , '03' , 98);
1. 查询课程编号为“01”的课程比“02”的课程成绩高的所有学生的学号(重点)
-- 难点在于表的连接
-- 1. 查询课程编号为“01”的课程:先把课程为01的学生找出来
-- 2. “02”的课程:再把课程为02的学生找出来
-- 3. 01 比 02 成绩高 :将两张表join起来做连接查询
select a.s_id 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
where a.s_score > b.s_score
2. 查询平均成绩大于60分的学生的学号和平均成绩
select s_id, avg(s_score)as avg_score
from score
GROUP BY s_id
having avg(s_score) > 60
-- 如果算上三门成绩的平均分>60分
select s_id, avg_score
from (
select s_id, sum(s_score)/3 as avg_score
from score
GROUP BY s_id
) a
where avg_score > 60
3. 查询所有学生的学号、姓名、选课数、总成绩
select a.s_id, a.s_name, count(b.c_id), sum(b.s_score)
from student a, score b
where a.s_id = b.s_id
group by a.s_id
select a.s_id, a.s_name, count(b.c_id), sum(b.s_score)
from student a
inner join score b
on a.s_id = b.s_id
group by a.s_id
4. 查询姓“猴”的老师的个数
-- _任意匹配一个字符,% 任意匹配多个字符
-- =匹配一个准确值,like匹配一个范围
select count(t_id)
from teacher
where t_name like '猴%'
5. 查询没学过“张三”老师课的学生的学号、姓名(重点)
注:子查询
-- 1. 查询张三老师的课
-- 2. 找出没上过老师课的学生
select *
from student
where s_id not in (
select s.s_id from score s
inner join course c on c.c_id = s.c_id
inner join teacher t on t.t_id = c.t_id
where t.t_name = "张三"
)
-- 使用join的写法
select s1.*
from student as s1
left join (
select s.s_id from score s
inner join course c on c.c_id = s.c_id
inner join teacher t on t.t_id = c.t_id
where t.t_name = "张三"
) as s2
on s1.s_id =s2.s_id
where s2.s_id is null
6. 查询学过“张三”老师所教的所有课的同学的学号、姓名(重点)
注:子查询
-- 1. 查询张三老师的课
-- 2. 找出上过老师课的学生
select *
from student
where s_id in (
select s.s_id from score s
inner join course c on c.c_id = s.c_id
inner join teacher t on t.t_id = c.t_id
where t.t_name = "张三"
)
7. 查询学过编号为“01”的课程并且也学过编号为“02”的课程的学生的学号、姓名(重点)
-- 思路:把01、02课程的学号分别子查询出来 ,
-- 然后inner join 或者 INTERSECT 将表的公共区域进行合并
select * 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
)
-- 效率优化(不要使用in关键字,会导致索引失效,耗时长)
-- from score inner join score
select * from student s
inner join (
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
) b
where s.s_id = b.s_id
相似题目:查询学过编号为“01”的课程并且但没有学过编号为“02”的课程的学生
-- 1. 先用student表左连接学过课程'01'的和学过课程'02'
select s.*, sc1.s_score, sc2.s_score from student s
left join (select * from score where c_id = '01') sc1 on s.s_id = sc1.s_id
left join (select * from score where c_id = '02') sc2 on s.s_id = sc2.s_id
-- 2. 筛选学过课程'01'但没有学过课程'02'的学生
where sc1.c_id = '01' and sc2.c_id is null;
8. 查询课程编号为“02”的总成绩
select c_id, sum(s_score)
from score
where c_id = '02'
-- 最好用groupby + having
-- (子查询的效率一般低于关联查询,所以尽量用表联接来代替不必要的子查询。)
-- 求和,求平均,求数量
select c_id, sum(s_score), avg(s_score), count(s_score)
from score
GROUP BY c_id
HAVING c_di = '02'
9. 查询所有课程成绩小于60分的学生的学号、姓名
-- 1. 查询最大的课程成绩小于60分的学生id
-- 2. 进行表连接
select s.s_id, s_name
from student s
inner join (
select s_id
from score
GROUP BY s_id
having max(s_score) < 60
) as b
on s.s_id = b.s_id
10. 查询没有学全所有课的学生的学号、姓名(重点)
-- 1. 分组统计,成绩数量小于课程的数量就是没学全(错误答案)
select s.s_id, s.s_name from student s
INNER JOIN (
select s_id from score
group by s_id
having count(distinct c_id) < (
select count(distinct c_id) from course
)
) as b
on s.s_id = b.s_id
-- 上面的做法有错误:如果有个人一门也没有选,则不会出现在score表中
-- 正确做法:使用left join将学生表和成绩表合并起来
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
)
11. 查询至少有一门课与学号为“01”的学生所学课程相同的学生的学号和姓名(重点)
-- 1. 查学号01所学的课程
-- 2. 查询IN 上面所学课程的学号
-- 3. 和学生信息表并表
select st.s_id, st.s_name from student as st
INNER JOIN (
select distinct sc1.s_id from score as sc1
inner join
(select c_id from score where s_id = '01') as sc2
on sc1.c_id = sc2.c_id
where s_id != '01') as s
on st.s_id = s.s_id
12. 查询和“01”号同学所学课程完全相同的其他同学的学号(重点)
-- 1. 找出修了与01号同学不同课的同学
-- 2. 找出修课数量与01同学相同的同学id
-- 3. 去除掉条件1中的同学
-- 4. 查出同学的信息
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')
)
13. 查询没学过"张三"老师讲授的任一门课程的学生姓名(重点,同5)
select s1.*
from student as s1
left join (
select s.s_id from score s
inner join course c on c.c_id = s.c_id
inner join teacher t on t.t_id = c.t_id
where t.t_name = "张三"
) as s2
on s1.s_id =s2.s_id
where s2.s_id is null
15. 查询两门及其以上不及格课程的同学的学号,姓名及其平均成绩(重点)
-- 1. 统计学生不及格的成绩数量 >= 2 和成绩
-- 2. 与学生表并表
select st.* , s.avg_score from student as st
inner join (
select s_id, count(c_id), avg(s_score) as avg_score from score
where s_score < 60
GROUP BY s_id
having count(distinct c_id) >= 2
) as s
on st.s_id = s.s_id
16. 检索"01"课程分数小于60,按分数降序排列的学生信息(与3.4题重复)
select st.*, s.c_id, s.s_score from student st
inner JOIN score as s
on st.s_id = s.s_id
where s.c_id = '01' and s.s_score < 60
order by s_score desc
-- 拓展:要同时查询出没考01的学生呢? 左表连接
select st.*, s.c_id, s.s_score from student st
left JOIN score as s
on st.s_id = s.s_id
where (s.c_id = '01' or s.c_id is null) and (s.s_score < 60 or s.s_score is null)
order by s_score desc
17、按平均成绩从高到低显示所有学生的所有课程的成绩以及平均成绩(重重点与35一样)
备注:(对分别求和时往往会用到case when)
1.因为要选出需要的字段 用case when 当c_id=‘01’ then 可以得到对应的 s_core
2.因为GROUP UP 要与select 列一致,所以case when 加修饰max
3.因为最后要展现出每个同学的各科成绩为一行,所以用到case
-- 普通写法 学号 课程号 课程得分 平均分(不满足业务需求)
select s1.s_id, s1.c_id, s1.s_score, s2.avg_score from score as s1
inner join (
select s_id, avg(s_score) as avg_score from score
group by s_id
)as s2
on s1.s_id = s2.s_id
ORDER BY s2.avg_score desc
-- 期望的写法 学号 课程1 课程2 课程3 平均分(如何行转列?)
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,最高分,最低分,平均分,及格率,中等率,优良率,优秀率 (超级重点)
–及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90 (超级重点)
-- 及格率: count(>=60) / count(*)
select s.c_id
, c.c_name
, max(s.s_score) "最高分"
, min(s.s_score) "最低分"
, avg(s.s_score) "平均分"
, sum(case when s.s_score >= 60 then 1 else 0 end) / count(s_id) "及格率"
, sum(case when s.s_score >= 70 and s.s_score < 80 then 1 else 0 end) / count(s_id) "中等率"
, sum(case when s.s_score >= 80 and s.s_score < 90 then 1 else 0 end) / count(s_id) "优良率"
, sum(case when s.s_score >= 90 then 1 else 0 end) / count(s_id) "优秀率率"
from score as s
inner join course as c on s.c_id = c.c_id
group by c_id
19. 按各科成绩进行排序,并显示排名(重点)
重点:row_number()over ([partition by 字段(按照字段分组)]order by 列)
mysql8.0窗口函数:https://developer.aliyun.com/article/593698
row_number 排序值从小到大,依次排列 1234
dense_rank 相同数据,并列保存,不存在断值(一直连续) 1223
rank 相同数据并列保存,下一个值跳值(断续) 1224
SELECT *
, ROW_NUMBER() OVER(PARTITION BY c_id order by s_score desc) as 'rank'
from score
20. 查询学生的总成绩并进行排名
select s_id, sum(s_score)
from score
GROUP BY s_id
order by sum(s_score) desc
21. 查询不同老师所教不同课程平均分从高到低显示
select b.t_id, b.t_name, s.c_id, b.c_name, avg(s.s_score) from score as s
inner join (
select distinct sc.c_id, c.c_name, t.t_id, t.t_name 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
) as b on s.c_id = b.c_id
GROUP BY s.c_id
ORDER BY avg(s.s_score) desc
22. 查询所有课程的成绩第2名到第3名的学生信息及该课程成绩(重要 25类似)
总结:
1.row_number () over (partition by 分组列 order by 排序列)
2.先分组后排序,作为一个表
3.从表中筛选出某行,用where
备注:如果想选出学生信息,那成绩,就是以列的形式出现,如果1.2.3名以行的形式出现,就不能有对应的学生信息。(见25题
-- 1. 按照课程分组进行排序(同19题)
-- 2. 找出排名第二第三的
select *
from (
select st.s_id, st.s_name, st.s_birth, st.s_sex, c_id, s_score
, ROW_NUMBER() OVER(PARTITION BY c_id ORDER BY s_score DESC) m
from score sc
INNER JOIN student st on sc.s_id = st.s_id
) a
where m in (2,3)
23. 使用分段[100-85],[85-70],[70-60],[<60]来统计各科成绩,分别统计各分数段人数:课程ID和课程名称(重点和18题类似)
select sc.c_id, c.c_name
, sum(case when sc.s_score < 60 then 1 else 0 end) "x<60"
, sum(case when sc.s_score >=
目录
- (1)查询每个区域的用户数
- (2)查询每个区域的男女用户数
- (3)查询姓张的用户数
- (4)筛选出id3~id5的用户
- (5)筛选出绩效不达标的员工
- (6)筛选出姓张的且绩效不达标的员工
- (7)查询获得销售冠军超过两次的人
- (8)查询某部门一年的月销售额最高涨幅
- (9)查询每个季度绩效得分大于70分的员工
- (10)删除重复值
- (11)行列互换
- (12)多列比较
- (13)对成绩进行分组
- (14)周累计数据获取
- (15)周环比数据获取
- (16)查询获奖员工信息
- (17)计算用户留存情况
- (18)筛选最受欢迎的课程
- (19)筛选出每个年级最受欢迎的三门课程
- (20)求累积和
- (21)获取新增用户数
- (22)获取用户首次购买时间
- (23)同时获取用户和订单数据
- (24)随机抽样
- (25)获取沉默用户数
- (26)获取新用户的订单数
- (27)获取借款到期名单
- (28)获取即将到期的借款信息
- (29)获取历史逾期借款信息
- (30)获取部门工资最高的员工
开发工具:
- mysql-8.0
- DataGrip
(1)查询每个区域的用户数
数据源:stu_table.csv
id,name,class,sex
4,张文华,二区,男
3,李思雨,一区,女
1,王小凤,一区,女
7,李智瑞,三区,男
6,徐文杰,二区,男
8,徐雨秋,三区,男
5,张青云,二区,女
9,孙皓然,三区,男
10,李春山,三区,男
2,刘诗迪,一区,女
需求:我们想知道每个区域有多少用户
解题思路:首先需要对区域进行分组,使用的是group by,然后对每个组内的用户进行计数聚合运算,使用的是count,最后运行结果如下表所示。
select
class,
count(id) as stu_sum
from
test.stu_table
group by
class;
运行结果:
(2)查询每个区域的男女用户数
数据源:stu_table.csv
需求:我们想知道每个区域内男生、女生分别有多少个。
-- 2.查询每个区域的男女用户数
-- 写法一
select class,sex,count(sex) from test.stu_table group by class,sex;
-- 写法二
select
class ,
count(case when sex = '男' then class end ) as '男',
count(case when sex = '女' then class end ) as '女'
from
test.stu_table
group by class;
-- 写法三
select
sex ,
count(case when class = '一区' then sex end ) as '一区',
count(case when class = '二区' then sex end ) as '二区',
count(case when class = '三区' then sex end ) as '三区'
from
test.stu_table
group by sex;
运行结果:
(3)查询姓张的用户数
数据源:stu_table.csv
需求:我们想知道这张表中姓张的用户有多少个?
select
count(id) as stu_num
from
test.stu_table
where name like '张%';
运行结果:
(4)筛选出id3~id5的用户
数据源:stu_table.csv
需求:我们想要获取id按照从小到大的顺序排列以后id3~id5的用户的信息。
-- 4.筛选出id3~id5的用户
select * from test.stu_table order by id limit 2,3;
运行结果:
(5)筛选出绩效不达标的员工
数据源:score_table.csv
id,namr,group,score
1,王小凤,一部,88
2,刘诗迪,一部,70
3,李思雨,一部,92
4,张文华,二部,55
5,张青云,二部,77
6,徐文杰,二部,77
7,李智瑞,三部,56
8,徐雨秋,三部,91
9,孙皓然,三部,93
10,李春山,三部,57
需求:我们想把绩效不达标(绩效得分小于60分)的员工的信息筛选出来。
select * from test.score_table where score < 60;
运行结果:
(6)筛选出姓张的且绩效不达标的员工
数据源:score_table.csv
需求:我们现在想根据这张表筛选出姓张的且绩效不达标的员工的信息。
-- 6.筛选出姓张的且绩效不达标的员工
select * from test.score_table where score < 60 and name like '张%';
运行结果:
(7)查询获得销售冠军超过两次的人
数据源:month_table.csv
id,name,month_num
E002,王小凤,1
E001,张文华,2
E003,孙皓然,3
E001,张文华,4
E002,王小凤,5
E001,张文华,6
E004,李智瑞,7
E002,王小凤,8
E003,孙皓然,9
需求:现在需要查询获得销售冠军的次数超过2次的人及其获得销售冠军的次数。
select
id,
name,
count(month_num) as num
from
test.month_table
group by
id,
name
having
num > 2;
运行结果:
(8)查询某部门一年的月销售额最高涨幅
数据源:sale_table.csv
year_num,month_num,sales
2019,1,2854
2019,2,4772
2019,3,3542
2019,4,1336
2019,5,3544
2018,1,2293
2018,2,2559
2018,3,2597
2018,4,2363
需求:现在我们想查询2019年的月销售额最高涨幅是多少。
select
year_num,
max(sales) as max_sales,
min(sales) as min_sales,
(max(sales) - min(sales)) as cha,
((max(sales) - min(sales)) / min(sales)) as growth
from
test.sale_table
group by
year_num;
运行结果:
(9)查询每个季度绩效得分大于70分的员工
数据源:score_info_table.csv
id,name,subject,score
1,王小凤,第一季度,88
1,王小凤,第二季度,55
1,王小凤,第三季度,72
3,徐雨秋,第一季度,92
3,徐雨秋,第二季度,77
3,徐雨秋,第三季度,93
2,张文华,第一季度,70
2,张文华,第二季度,77
2,张文华,第三季度,91
解题思路:我们要查询的是每个季度绩效得分都大于70分的员工,只要能够保证每个季度每位员工的最小绩效得分是大于70分的,就可以说明这位员工的每个季度绩效得分都大于70分。
需求:现在我们想要通过这张表查询每个季度绩效得分都大于70分的员工。
select
id,
name,
min(score) as min_score
from
test.score_info_table
group by
id,
name
having min_score > 70;
运行结果:
(10)删除重复值
数据源:stu_info_table.csv
id,name,t_1,t_2
1,王小凤,产品技术部,B端产品
2,刘诗迪,产品技术部,C端产品
3,李思雨,产品技术部,B端产品
5,张青云,销售运营部,数据分析
4,张文华,销售运营部,销售管理
6,徐文杰,销售运营部,销售管理
7,李智瑞,产品技术部,B端产品
8,徐雨秋,销售运营部,销售管理
9,孙皓然,产品技术部,B端产品
需求:现在我们想获取该公司一级部门及二级部门的信息,即哪些一级部门下包含哪些二级部门
select
t_1,
t_2
from
test.stu_info_table
group by
t_1,
t_2
order by t_1;
运行结果:
(11)行列互换
数据源:row_col_table.csv
year_num,month_num,sales
2019,1,100
2019,2,200
2019,3,300
2019,4,400
2020,1,200
2020,2,400
2020,3,600
2020,4,800
需求:我们需要把如上表所示的纵向存储数据的方式改成如下表所示的横向存储数据的方式。
解题思路:首先按照year_num分组,利用case when xxx then sales end条件控制语句,当month_num = 1时返回sales,以此类推,得到列值。
-- 11.行列互换
select * from test.row_col_table;
select year_num,
sum(case when month_num = 1 then sales end ) as m1,
sum(case when month_num = 2 then sales end ) as m2,
sum(case when month_num = 3 then sales end ) as m3,
sum(case when month_num = 4 then sales end ) as m4
from test.row_col_table group by year_num;
运行结果:
(12)多列比较
数据源:col_table.csv
col_1,col_2,col_3
5,10,7
1,10,6
9,3,5
5,2,9
10,4,3
5,2,9
5,8,6
8,8,6
需求:我们需要根据这三列数据生成一列结果列,结果列的生成规则为:如果col_1列大于col_2列,则结果为col_1列的数据;如果col_2列大于col_3列,则结果为col_3列的数据,否则结果为col_2列的数据。
解题思路:多列比较其实就是一个多重判断的过程,借助case when即可实现,先判断col_1 列和col_2列的关系,然后判断col_2列和col_3列的关系。这里需要注意的是,判断的执行顺序是先执行第一行case when,然后执行第二行case when,最后运行结果如下表所示。
select col_1,
col_2,
col_3,
(case
when col_1 > col_2 then col_1
when col_2 > col_3 then col_3 end ) as result_col
from
test.col_table;
运行结果:
(13)对成绩进行分组
数据源:subject_table.csv
id,score
1,56
2,91
3,67
4,54
5,56
6,69
7,61
8,83
9,99
需求:我们想知道60分以下(不包含60分)、60~80分(不包含80分)、80~100分三个成绩段内分别有多少个学生
解题思路:写法一通过case when写法,分别求出各个分数段的个数作为列;写法二利用的是case when,完成成绩分段以后再对分段结果进行group by,接着在组内计数获得每个成绩段内的学生数
-- 写法一
select * from test.subject_table;
select
count(case when score >= 80 then score end ) as '80~100分',
count(case when score >= 60 and score < 80 then score end ) as '60~80分',
count(case when score < 60 then score end ) as '60分以下'
from test.subject_table;
-- 写法二
select
(case
when score >= 80 then '80~100分'
when score >= 60 and score < 80 then '60~80分'
when score < 60 then '60分以下' end ) as score_bin ,
count(case when score >= 80 then score
when score >= 60 and score < 80 then score
when score < 60 then score end) as count
from test.subject_table group by score_bin;
运行结果:
(14)周累计数据获取
数据源:order_table.csv
order_id,order_date
1,2019/1/8
2,2019/1/9
3,2019/1/10
4,2019/1/11
5,2020/1/8
6,2020/1/9
7,2020/1/10
8,2020/1/11
9,2020/1/12
需求:现在每天需要获取本周累计的订单数,本周累计是指本周一到获取数据当天,比如,今天是周三,那么本周累计就是周一到周三,该怎么实现呢?
解题思路:我们要获取本周累计的订单数,只需要把本周的订单明细筛选出来,然后对订单ID进行计数即可。
-- 14.周累计数据获取
select * from test.order_table;
-- 现在每天需要获取本周累计的订单数,本周累计是指本周一到获取数据当天,比如,今天是周三,那么本周累计就是周一到周三,该怎么实现呢?
update order_table set order_date = '2021/04/24' where order_id = 6;
update order_table set order_date = '2021/04/28' where order_id = 7;
update order_table set order_date = '2021/04/29' where order_id = 8;
update order_table set order_date = '2021/04/30' where order_id = 9;
select date_format(order_date,'%Y-%m-%d') from test.order_table;
select weekofyear(date_format(order_date,'%Y-%m-%d')) from test.order_table;
select weekofyear(current_date) as '本周';
select year(order_date) as '年' from test.order_table;
select week(order_date) as '周' from test.order_table;
select
year(order_date) as '年',
week(order_date) as '周',
count(order_id)
from
test.order_table
where
year(order_date) = year(current_date)
and week(order_date) = weekofyear(current_date)
group by
year(order_date),
week(order_date);
运行结果:
(15)周环比数据获取
数据源:order_table.csv
需求:获取当日的订单数和当日的环比订单数(即昨日的数据)
select
count(case when date(order_date) = date (current_date) then order_id end ) as order_count,
count(case when date_sub(date (current_date),interval 1 day ) = order_date then order_id end) as last_order_count
from
test.order_table;
运行结果:
(16)查询获奖员工信息
数据源:16_table1.csv、16_table2.csv
id,name
1,王小凤
2,刘诗迪
3,李思雨
4,张文华
5,张青云
6,徐文杰
7,李智瑞
8,徐雨秋
9,孙皓然
id,name
1,王小凤
2,刘诗迪
3,李思雨
7,李智瑞
8,徐雨秋
9,孙皓然
需求:现在我们想通过table1表获取获奖员工的更多信息。
select * from test.`16_table1` inner join `16_table2` `16t2` on `16_table1`.id = `16t2`.id;
运行结果:
(17)计算用户留存情况
数据源:user_login.csv
uid,login_time
1,2021/4/21 6:00
1,2021/4/24 10:00
1,2021/4/25 19:00
2,2021/4/22 10:00
2,2021/4/28 9:00
2,2021/4/29 14:00
3,2021/4/27 8:00
3,2021/4/28 10:00
需求:我们想看用户的次日留存数、三日留存数、七日留存数(只要用户首次登录以后再登录就算留存下来了),该怎么实现呢?
解题思路:本题有两种解题思路,先看思路一:按照用户时间求出七日留存,首先按uid分组,求出每个uid的第一次登陆时间和最后一次登陆时间,算出中间间隔的时间,如果间隔为1就是次日留存,间隔为3就是3日留存,间隔为7就是7日留存,以此类推分别求出他们的数量。思路二:按照当日时间求出七日留存,如果用户登陆的时间正好等于当前日期前一天的日期,则去重统计uid数量即为次日留存数,以此类推分别求出三日留存、七日留存。
以上是关于SQL面试必会50题的主要内容,如果未能解决你的问题,请参考以下文章