Files
CategoryResourceRepost/极客时间专栏/MySQL 必知必会/特别放送/特别发送(一) | 经典面试题讲解第一弹.md
louzefeng d3828a7aee mod
2024-07-11 05:50:32 +00:00

253 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<audio id="audio" title="特别发送(一) | 经典面试题讲解第一弹" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/df/83/df7d7cff7a07c33c5bbf098c40ff1283.mp3"></audio>
你好,我是朱晓峰。
到这里“实践篇”的内容咱们就学完了。今天我们来学点儿不一样的——5道经典面试题。这些都是在实际面试中的原题当然我没有完全照搬而是结合咱们课程的具体情况有针对性地进行了调整。我不仅会给你提供答案还会和你一起分析让你能够灵活地吃透这些题目并能举一反三。
话不多说,我们现在开始。我先带你从一道简单的关于“索引”的面试题入手,索引在面试题里经常出现,来看看这一道你能不能做对。
## 第一题
下面关于索引的描述,正确的是:
1. 建立索引的主要目的是减少冗余数据,使数据表占用更少的空间,并且提高查询的速度
1. 一个表上可以建立一个或者多个索引
1. 组合索引可以有效提高查询的速度,比单字段索引更高效,所以,我们应该创建一个由所有的字段组成的组合索引,这样就可以解决所有问题了
1. 因为索引可以提高查询效率,所以索引建得越多越好
解析这道题的正确答案是选项2我们来分析一下其他选项。
- 选项1说对了一半索引可以提高查询效率但是创建索引不能减少冗余数据而且索引还要占用额外的存储空间所以选项1不对。
- 选项3不对的原因有2个。第一组合索引不一定比单字段索引高效因为组合索引的字段是有序的遵循左对齐的原则。如果查询的筛选条件不包含组合索引中最左边的字段那么组合索引就完全不能用。第二创建索引也是有成本的需要占用额外的存储空间。用所有的字段创建组合索引的存储成本比较高而且利用率比较低完全用上的可能性几乎不存在所以很少有人会这样做。而且一旦更改任何一个字段的数据就必须要改索引这样操作成本也比较高。
- 选项4错误因为索引有成本很少作为筛选条件的字段没有必要创建索引。
如果这道题你回答错了,一定要回去复习下[第11讲](https://time.geekbang.org/column/article/357312)的内容。
## 第二题
假设我们有这样一份学生成绩单所有同学的成绩都各不相同请编写一个简单的SQL语句查询分数排在第三名的同学的成绩
<img src="https://static001.geekbang.org/resource/image/36/d1/3618ee4c82a592bb7954c4d63d9c1dd1.jpeg" alt="">
解析:这道题考查的是我们对查询语句的掌握情况。针对题目中的场景,可以分两步来进行查询。
第一步,按照成绩高低进行排序:
```
mysql&gt; SELECT *
-&gt; FROM demo.test1
-&gt; ORDER BY score DESC; -- DESC表示降序排列
+----+------+-------+
| id | name | score |
+----+------+-------+
| 2 | 李四 | 90.00 |
| 4 | 赵六 | 88.00 |
| 1 | 张三 | 80.00 |
| 3 | 王五 | 76.00 |
| 5 | 孙七 | 67.00 |
+----+------+-------+
5 rows in set (0.00 sec)
```
第二步,找出排名第三的同学和对应的成绩。我们可以用[第4讲](https://time.geekbang.org/column/article/351225)里学过的对返回记录进行限定的关键字LIMIT
```
mysql&gt; SELECT *
-&gt; FROM demo.test1
-&gt; ORDER BY score DESC
-&gt; LIMIT 2,1;
+----+------+-------+
| id | name | score |
+----+------+-------+
| 1 | 张三 | 80.00 |
+----+------+-------+
1 row in set (0.00 sec)
```
在MySQL中LIMIT后面可以跟2个参数第一个参数表示记录的起始位置第一个记录的位置是0第二个参数表示返回几条记录。因此“LIMIT 2,1”就表示从第3条记录开始返回1条记录。这样就可以查出排名第三的同学的成绩了。
## 第三题
现在我们有两个表分别是人员表demo.person和地址表demo.address要求你使用关联查询查出完整信息。无论有没有地址信息人员的信息必须全部包含在结果集中。
人员表:
<img src="https://static001.geekbang.org/resource/image/b6/90/b6ed67787cebbdb0786a42c47326a390.jpeg" alt="">
地址表:
<img src="https://static001.geekbang.org/resource/image/57/ea/5758763942c2a0dc59125bd73f3134ea.jpeg" alt="">
解析: 这个是典型的外查询,咱们在[第6讲](https://time.geekbang.org/column/article/353464)里学过。题目要求我们查出人员表中的全部信息,而地址表中信息则可以为空,就可以用下面的查询代码:
```
mysql&gt; SELECT *
-&gt; FROM demo.person AS a
-&gt; LEFT JOIN demo.address AS b -- 左连接确保demo.person中的记录全部包括在结果集中
-&gt; ON (a.id=b.id);
+----+-------+-------+------+---------+------+-----------+
| id | fname | lname | id | country | city | address |
+----+-------+-------+------+---------+------+-----------+
| 1 | 张 | 三 | 1 | 中国 | 北京 | 海淀123 |
| 2 | 李 | 四 | 2 | 美国 | 纽约 | 奥巴尼333 |
| 3 | 王 | 五 | NULL | NULL | NULL | NULL |
+----+-------+-------+------+---------+------+-----------+
3 rows in set (0.02 sec)
```
## 第四题
假设有这样一个教学表demo.teach)里面包含了人员编号id、姓名fname和对应的老师的人员编号teacherid。如果一个人是学生那么他一定有对应的老师编号通过这个编号就可以找到老师的信息如果一个人是老师那么他对应的老师编号就是空。比如说下表中李四的老师编号是101我们就可以通过搜索人员编号找到101的名称是张三那么李四的老师就是张三而张三自己就是老师所以他对应的老师编号是空。
<img src="https://static001.geekbang.org/resource/image/8d/7c/8d9de75354c0af5bc99e01767b87a57c.jpeg" alt="">
要求请写一个SQL语句查询出至少有2名学生的老师姓名。
说明一下在刚刚的数据表中张三有3名学生分别是李四、王五和周八。赵六有一名学生是孙七。因此正确的SQL语句的查询结果应该是
<img src="https://static001.geekbang.org/resource/image/e5/40/e515868404495d2cea1c271b0d0ec440.jpeg" alt="">
解析:
针对这道题,我们可以按照这样的思路去做:
1. 通过统计学生对应的老师编号就可以获取至少有2个学生的老师的编号。
1. 通过关联查询和自连接获取需要的信息。所谓的自连接就是数据表与自身进行连接。你可以认为是把数据表复制成一模一样的2个表通过给数据表起不同的名字来区分它们这样方便对表进行操作然后就可以对这2个表进行连接操作了。
1. 通过使用条件语句WHERE和HAVING对数据进行筛选先用WHERE筛选出所有的老师编号再用HAVING筛选出有2个以上学生的老师编号。
首先,我们来获取老师编号,如下:
```
mysql&gt; SELECT teacherid
-&gt; FROM demo.teach
-&gt; WHERE teacherid is not NULL -- 用WHERE筛选出所有的老师编号
-&gt; GROUP BY teacherid
-&gt; HAVING COUNT(*)&gt;=2; -- 用HAVING筛选出有2个以上学生的老师编号
+-----------+
| teacherid |
+-----------+
| 101 |
+-----------+
1 row in set (0.00 sec)
```
接着,通过自连接,来获取老师的姓名:
```
mysql&gt; SELECT a.id,a.fname
-&gt; FROM demo.teach AS a
-&gt; JOIN
-&gt; (
-&gt; SELECT teacherid
-&gt; FROM demo.teach
-&gt; WHERE teacherid IS NOT NULL
-&gt; GROUP BY teacherid
-&gt; HAVING COUNT(*)&gt;=2
-&gt; ) AS b
-&gt; ON (a.id = b.teacherid);
+-----+-------+
| id | fname |
+-----+-------+
| 101 | 张三 |
+-----+-------+
1 row in set (0.00 sec)
```
## 第五题
假设某中学高三年级有多位同学分成多个班我们有统一记录学生成绩的表demo.student)和班级信息表demo.class具体信息如下所示
学生成绩表:
<img src="https://static001.geekbang.org/resource/image/9a/20/9ae0eyy03386f24d568b8507d2dd6f20.jpeg" alt="">
班级信息表:
<img src="https://static001.geekbang.org/resource/image/6c/b9/6c4d85c4dff2c626d55fbaf9671bccb9.jpeg" alt="">
要求写一个SQL查询语句查出每个班级前三名的同学。
说明一下针对上面的数据正确的SQL查询应该得出下面的结果
<img src="https://static001.geekbang.org/resource/image/5c/cd/5c6fc34826c367f5a0cdf38610b26ecd.jpeg" alt="">
解析:
1. 从题目给出的查询结果看不需要考虑并列的情况。那么现在要选出分数排名前三的同学其实只要找出3个最好的分数以及对应的同学就可以了。
1. 这道题需要用到我们讲过的关联查询和子查询的知识。
1. WHERE语句的筛选条件表达式中也可以包括一个子查询的结果。
第一步我们假设有一个分数X就是那个第N好的分数算一下有多少个同学的成绩优于这个分数
```
SELECT COUNT(DISTINCT b.points)
FROM demo.student AS b
WHERE b.points &gt; X;
```
这个查询的结果小于3的话就代表这个分数X是排名第三的分数了。
第二步,查询出哪些同学满足成绩排名前三的这个档次:
```
mysql&gt; SELECT a.stdname,a.points
-&gt; FROM demo.student AS a
-&gt; WHERE 3 &gt; -- 比这个成绩好的不超过3说明这是第三好的成绩
-&gt; (
-&gt; SELECT COUNT(DISTINCT b.points) -- 统计一下有几个成绩
-&gt; FROM demo.student AS b
-&gt; WHERE b.points &gt; a.points -- 比这个成绩好
-&gt; );
+---------+--------+
| stdname | points |
+---------+--------+
| 张三 | 85 |
| 李四 | 80 |
| 赵六 | 90 |
| 周八 | 85 |
+---------+--------+
4 rows in set (0.00 sec)
```
第三步,与班级表关联,按班级统计前三名同学的成绩,并且获取班级信息:
```
mysql&gt; SELECT c.classname,a.stdname,a.points
-&gt; FROM demo.student AS a
-&gt; JOIN demo.class AS c
-&gt; ON (a.classid = c.id) -- 关联班级信息
-&gt; WHERE 3 &gt;
-&gt; (
-&gt; SELECT COUNT(DISTINCT b.points)
-&gt; FROM demo.student AS b
-&gt; WHERE b.points &gt; a.points
-&gt; AND b.classid = a.classid -- 按班级分别查询
-&gt; )
-&gt; ORDER BY c.classname,a.points DESC;
+-----------+---------+--------+
| classname | stdname | points |
+-----------+---------+--------+
| 创新班 | 赵六 | 90 |
| 创新班 | 张三 | 85 |
| 创新班 | 周八 | 85 |
| 创新班 | 郑九 | 70 |
| 普通班 | 李四 | 80 |
| 普通班 | 王五 | 65 |
+-----------+---------+--------+
6 rows in set (0.00 sec)
```
## 总结
今天我们借助几个面试题回顾了索引的概念、查询、子查询和关联查询的知识以及条件语句WHERE和HAVING的不同使用方法。如果你发现哪些内容掌握得还没有那么牢固一定要及时回去复习一下。
在真正的面试中,很少有单纯考查知识点本身的题目,更多的是考查你在解决实际问题的过程中,对知识的灵活运用能力。所以,在学习每一节课时,你一定要结合我给出的实际项目,去真正实践一下,这样才能以不变应万变,在面试中有好的表现。