前言
本篇跟着知乎文章SQL面试必会50题 - 知乎 的顺序记录SQL刷题过程。
参考资料:
在每道题的标题后面会有一些符号:
无任何符号:没有思路 or 解题过程中参考了一些题解思路
✔:Acess!
❗:在编写代码的过程中遇到一些语法上的问题,或者是一些巧妙的解题思路,需要留意
每题的题号后会增加重点 或超级重点 的标识,需要特别留意
题目数据依赖
本节将详细说明刷题所依赖的数据内容。
表结构和关联关系
题目所依赖的表结构和关系如上图所示👆
总共包含四张表:学生表、成绩表、课程表、教师表。
四张表的关联字段:
学生表(Student) - 成绩表(Score):Student.s_id = Score.s_id
【通过学号
关联】
成绩表(Score) - 课程表(Course):Score.c_id = Course.c_id
【通过课程编号
关联】
课程表(Course) - 教师表(Teacher):Course.t_id = Teacher.t_id
【通过教师号
关联】
建表和测试数据插入语句
建库
数据库属性如下:
数据库名:50_SQL_Practice_Exercises
字符集:utf8
排序规则:utf8_unicode_ci
建表
学生表:
1 2 3 4 5 6 7 8 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`));
课程表:
1 2 3 4 5 6 7 CREATE TABLE `Course`(`c_id` VARCHAR (20 ), `c_name` VARCHAR (20 ) NOT NULL DEFAULT '' , `t_id` VARCHAR (20 ) NOT NULL , PRIMARY KEY(`c_id`));
教师表:
1 2 3 4 5 6 CREATE TABLE `Teacher`(`t_id` VARCHAR (20 ), `t_name` VARCHAR (20 ) NOT NULL DEFAULT '' , PRIMARY KEY(`t_id`));
成绩表:
1 2 3 4 5 6 7 CREATE TABLE `Score`(`s_id` VARCHAR (20 ), `c_id` VARCHAR (20 ), `s_score` INT (3 ), PRIMARY KEY(`s_id`,`c_id`));
测试数据
学生表测试数据:
1 2 3 4 5 6 7 8 9 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' , '女' );
课程表测试数据:
1 2 3 4 insert into Course values ('01' , '语文' , '02' );insert into Course values ('02' , '数学' , '01' );insert into Course values ('03' , '英语' , '03' );
教师表测试数据:
1 2 3 4 insert into Teacher values ('01' , '张三' );insert into Teacher values ('02' , '李四' );insert into Teacher values ('03' , '王五' );
成绩表测试数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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 );
题目
第一题【重点】
题目描述:查询课程编号为“01”的课程比“02”的课程成绩高的所有学生的学号
题解:
1 2 3 4 5 6 7 SELECT table_A.s_id FROM (SELECT s_id, c_id, s_score FROM score WHERE `c_id` = '01' ) AS table_A INNER JOIN (SELECT s_id, c_id, s_score FROM score WHERE `c_id` = '02' ) AS table_B ON table_A.s_id = table_B.s_id WHERE table_A.s_score > table_B.s_score;
思路:构建一个临时表,表中包含学生学号、01课程成绩、02课程成绩,通过在此表中创建简单的查询语句就可以实现
首先在成绩表中拿到01课程的信息:SELECT s_id, c_id, s_score FROM score WHERE c_id = '01'
然后在成绩表中拿到02课程的信息:SELECT s_id, c_id, s_score FROM score WHERE c_id = '02'
通过INNER JOIN
将两个表做内连接,连接字段为学生学号
接下来就是简单查询的步骤,查询字段为s_id
,查询条件为:table_A.s_score > table_B.s_score
第二题【重点】✔❗
题目描述:查询平均成绩大于60分的学生的学号和平均成绩
题解:
1 2 3 SELECT s_id, AVG (s_score) AS avg_score FROM score GROUP BY s_id HAVING avg_score > 60 ;
主要考察GROUP BY
的使用
注意❗:一般在查找字段中使用了函数,如果该字段在过滤条件中也使用到了,那么通常要给该字段增加别名,否则在过滤条件中又使用函数,会使得查找效率降低。
第三题❗
题目描述:查询所有学生的学号、姓名、选课数、总成绩
题解:
1 2 3 4 5 6 7 SELECT table_A.s_id, table_A.s_name, COUNT (table_B.c_id), SUM (CASE WHEN table_B.s_score IS NULL THEN 0 ELSE table_B.s_score END ) FROM (SELECT s_id, s_name FROM student) AS table_A LEFT JOIN (SELECT s_id, c_id, s_score FROM score) AS table_B ON table_A.s_id = table_B.s_id GROUP BY table_A.s_id, table_A.s_name;
思路:构建一个临时表,表中包含学生学号、学生姓名、课程号、课程成绩,通过在此表中创建简单的查询语句就可以实现
注意❗:
table_A(学生表)和table_B(成绩表)需要使用左连接LEFT JOIN
而非内连接INNER JOIN
,因为有可能学生没有任何成绩,那么通过内连接查询的话不会显示该学生。
为了更严谨,GROUP BY
中的字段应该是在查询字段中出现的,否则有可能报错,所以这里GROUP BY
同时使用table_A.s_id
和table_A.s_name
,其效果和仅使用其中一个字段是一样的。
如果学生没有成绩的话,则直接在查询中使用SUM(table_B.s_score)
会出现成绩为Null
的情况,为了避免,可以使用条件语句设置为0。
第四题✔
题目描述:查询姓“猴”的老师的个数
题解:
1 SELECT COUNT (* ) FROM teacher WHERE t_name LIKE '猴%' ;
简单查询
第五题【重点】❗
题目描述:查询没学过“张三”老师课的学生的学号、姓名
题解:
1 2 3 4 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 = '张三' )))
思路:一步一步执行子查询
先从教师表(teacher)中找到姓名为“张三”老师的教师号t_id
再从课程表(course)中找到“张三”老师教的课的课程编号c_id
接下来从成绩表(score )中找到学过“张三”老师教的课的学生的学号s_id
最后从全体学生中排除学过该课的学生
注意❗:
正确的思路是先拿到学过“张三”老师课的学生,然后从全体学生中排除(针对此题的情况),因为判断学生是否学过课程是根据成绩表查询的,如果对应课程有成绩,则说明学生学过课程。但是不可以使用SELECT * FROM score WHERE c_id != '指定课程号'
,因为一个学生可能学过好多们课,那这种查询方式只能把学过指定课程号的记录给删除,该学生的其他记录还在,没有办法起到筛选的作用。
后记
To be continued…