MySQL经典50题全解析:从学生成绩模型到SQL查询实战
2026/9/13 10:10:58 网站建设 项目流程

提到MySQL经典50题,很多准备面试或者刚入门的同学应该不陌生。我当年第一次拿到这份题集时,根本没把它当回事,觉得就是网上流传的学生成绩查询练习,结果真正上手以后才发现,这套题几乎把一个后端工程师和数据分析师在日常工作中会遇到的基础SQL场景全给覆盖了:条件过滤、分组统计、多表连接、子查询、去重排序、分组取TopN……每一类都能在真实业务里找到对应。

这套题最吸引我的地方是它不考任何花哨的语法,所有问题都基于一张很简单的“学生-课程-成绩”模型。正因为模型足够简单,你才能把注意力完全放在SQL本身的写法上。不管是准备面试、复习基础,还是带新人练SQL,都很合适。下面我就按自己刷题三遍之后的经验,把这个题集背后的表结构设计、题型分类、踩坑细节和练习方法一次说清楚。

1. 这套题到底在练什么:整体设计与场景拆解

1.1 “经典50题”的构成

这套题在很多技术社区都能找到,版本之间可能题目编号略有出入,但核心模型高度一致:四张表、一个选课成绩系统。student表存学生基础信息,teacher表存教师信息,course表存课程信息,sc表存学生的选课记录和成绩。题目难度呈阶梯式递进,前几题基本是单表查询,中间开始多表关联,后段会出现聚合、子查询、分组排序、去重等组合题,少数版本还会加入日期处理、字符串处理和行列转换。

整体来看,50题把SQL查询中的常用知识点分成了几个层次,我用平时带人练习的习惯给它们做了个分类:

题目类型覆盖知识点常见问法
基础查询SELECT、WHERE、LIKE、IN、BETWEEN查询姓“张”的学生、查询成绩大于80分的学生
分组统计GROUP BY、聚合函数、HAVING统计每门课的平均分、统计每个学生的选课门数
多表连接INNER JOIN、LEFT JOIN、表别名查询选修了某门课程的学生名单
子查询标量子查询、IN子查询、EXISTS查询比某学生成绩高的同学
排序去重ORDER BY、DISTINCT、LIMIT查询成绩最高的前几名、去重查询教师名单
高级组合窗口函数、自连接、变量每门课成绩前两名,或按分数排名

对刚入门的人来说,这套题的梯度设计非常合理。你不需要一上来就面对复杂的业务表,也不用懂存储过程、触发器,只需要把DQL查询练熟,就能覆盖日常工作中90%的取数需求。

1.2 为什么选“学生-课程-成绩”这个模型

学生选课这个场景,本质上是一个标准的多对多关系建模:一个学生可以选多门课,一门课可以被多个学生选,sc表作为中间关系表,在记录选课关系的同时保存成绩。

这种结构在业务系统里太常见了,订单-商品、用户-角色、文章-标签,全都是同一个套路。你把表名换掉,50题里的查询逻辑直接就能迁移到电商后台、内容管理系统和运营报表里。比如“查询选修了数学课的学生”换到电商场景就是“查询购买了某个商品的用户”,“统计每门课的平均分”换到运营场景就是“统计每个品类的平均客单价”。

之所以很多人说SQL学不会,是因为一上来就对着几十个字段的大宽表,连业务含义都理不清,更别说写关联查询了。而学生成绩模型只有四张表,外键关系一目了然,是理解和掌握关系模型的最短路径。这其实也回答了热搜里那个“学生课程成绩信息实体表设计mysql”的问题,只要把这四张表的结构吃透,绝大部分教学型业务表的设计思路你都大概有数了。

1.3 什么人适合拿它练手

不同阶段的人用这套题,收获完全不一样。

准备面试的求职者,可以把50题当成高频面试题集来刷。很多面试官自己就刷过这套题,面试时随口改个条件,比如“查成绩第二高的学生”“查每门课的平均分排名”,本质上都在这套题的射程范围内。

在职的开发和数据分析师,如果平时写SQL全靠复制粘贴,那这套题补基础非常合适。我见过不少写业务SQL很熟练的同学,遇到NOT IN里混入NULL就翻车,遇到ONLY_FULL_GROUP_BY报错就慌,其实这些坑在50题里都有对应场景。

带新人的团队负责人也可以直接拿它当练习册,配一套标准数据,让新人自己建表、造数、刷题,比打印一堆文档管用得多。

2. 建表与数据准备:先把地基打对

2.1 四张核心表的字段设计与DDL

开始刷题之前,第一步一定是自己动手建表。别去网上复制现成的建表语句,自己敲一遍,你对字段类型、约束、主外键的理解会完全不一样。下面是我常用的建表脚本,适配MySQL 8.0,也基本兼容5.7:

CREATE TABLE student ( sid INT PRIMARY KEY AUTO_INCREMENT COMMENT '学生ID', sname VARCHAR(20) NOT NULL COMMENT '姓名', sage DATETIME COMMENT '出生日期', ssex VARCHAR(10) COMMENT '性别' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生表'; CREATE TABLE teacher ( tid INT PRIMARY KEY AUTO_INCREMENT COMMENT '教师ID', tname VARCHAR(20) NOT NULL COMMENT '教师姓名' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='教师表'; CREATE TABLE course ( cid INT PRIMARY KEY AUTO_INCREMENT COMMENT '课程ID', cname VARCHAR(30) NOT NULL COMMENT '课程名', tid INT COMMENT '授课教师ID', CONSTRAINT fk_course_teacher FOREIGN KEY (tid) REFERENCES teacher(tid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程表'; CREATE TABLE sc ( sid INT NOT NULL COMMENT '学生ID', cid INT NOT NULL COMMENT '课程ID', score DECIMAL(5,2) COMMENT '成绩', PRIMARY KEY (sid, cid), KEY idx_cid (cid), CONSTRAINT fk_sc_student FOREIGN KEY (sid) REFERENCES student(sid), CONSTRAINT fk_sc_course FOREIGN KEY (cid) REFERENCES course(cid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='选课成绩表';

几个字段设计上的细节说一下。

sid、tid、cid用INT主键自增,纯粹是为了查询方便和避免重复。sname、cname这类字符串字段,长度不用给太大,VARCHAR(20)和VARCHAR(30)足够,给太大反而浪费存储空间,还影响索引效率。sage用DATETIME而不是VARCHAR,这是很多人容易犯的错,日期字段一定要用日期类型,否则ORDER BY排序、日期函数计算的时候会非常痛苦。score用DECIMAL(5,2)而不是FLOAT或INT,因为成绩可能带小数,而且DECIMAL在计算平均值和求和时更精确,不会出现浮点数误差。

sc表的主键用复合主键(sid, cid),这样保证同一个学生同一门课只有一条选课记录,从数据库层面杜绝重复数据。这是数据建模里一个很重要的思想,选课关系天然是联合唯一的,把它设成复合主键,后面刷题时很多去重逻辑就不用手动写了。

2.2 建表时最常见的三个坑

第一,字符集必须用utf8mb4,不要用utf8。MySQL里的utf8其实是utf8mb3,最大只支持3字节,存emoji、生僻字、部分特殊符号时会报错或乱码。utf8mb4才是真正的四字节UTF-8。排序规则方面,MySQL 8.0默认是utf8mb4_0900_ai_ci,5.7默认是utf8mb4_general_ci,两者都不区分大小写,日常练习没太大区别。如果有人问你“为什么MySQL字符串比较不区分大小写”,答案就在排序规则里的_ci后缀,它表示case-insensitive,改成_bin或_cs后缀就会区分大小写了。

第二,外键约束可以建,但要知道真实项目中往往不建。练习环境建外键能帮你理清表间关系,插入数据时也能自动校验完整性。但生产环境外键会影响写入性能,尤其是大并发写入时,每插入一条子表记录,数据库都要去父表校验一遍,开销不小。很多互联网公司会在应用层保证数据一致性,表结构层面反而不建外键。这个区别面试时经常被问到,不要只是死记“生产不建外键”,要能解释清楚原因。

第三,默认存储引擎一定要是InnoDB。MySQL 5.5之后InnoDB就是默认引擎了,除非你主动改成MyISAM,否则一般不会踩坑。但如果是在老项目里,碰到表是MyISAM的,又没有主键,刷题时会发现很多在线DDL操作做不了,而且不支持事务。练习时统一用InnoDB,别给自己找麻烦。

2.3 造数据的实操技巧

建完表之后就是插入测试数据。网上各版本的50题自带的数据不一定完全一样,但为了保证练习效果,你自己造一套符合逻辑的数据即可,不用纠结是否和某个版本一模一样。

插入中文数据时最容易遇到乱码,根因往往是客户端连接字符集和表字符集不一致。用命令行或客户端工具连接时,先执行一句SET NAMES utf8mb4;,再插入中文就不会乱码。如果你用的是Navicat或DBeaver这类图形工具,一般在连接属性里设置好编码就不会有问题。

如果不想手工一条条插,可以用笛卡尔积批量造数。下面这段SQL可以在student表里生成1万个学生名,日常练习时用不着这么多,但理解这条语句本身就很有价值:

INSERT INTO student (sname, sage, ssex) SELECT CONCAT('学生', n), NOW(), '男' FROM ( SELECT a.n + b.n * 10 + c.n * 100 + d.n * 1000 AS n FROM (SELECT 0 n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) a CROSS JOIN (SELECT 0 n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) b CROSS JOIN (SELECT 0 n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) c CROSS JOIN (SELECT 0 n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) d ) t;

刷这套题时我反而建议插入的数据量控制在20到30条左右就行。数据量太大,结果对不对很难一眼看清;数据量太小,又覆盖不了各种边界情况。比如为了测出“NOT IN遇到NULL返回空集合”的坑,你必须在数据里故意留几个NULL成绩,否则压根不会遇到这类问题。

3. 经典题型拆解:从单表到多表的核心写法

3.1 单表查询与条件过滤:别小看WHERE

50题里最简单的部分就是单表查询,但越简单的东西越能看出基本功。题目通常会这样问:查询姓“张”的学生、查询成绩大于80分的记录、查询课程名以“数”开头的课程。

SELECT sname, ssex FROM student WHERE sname LIKE '张%'; SELECT sid, cid, score FROM sc WHERE score > 80; SELECT cid, cname FROM course WHERE cname LIKE '数%';

第一题考察LIKE通配符。%表示任意多个字符,_表示单个字符,所以LIKE '张%'能匹配“张三”“张伟”等所有张姓学生。第三题的LIKE '数%'同理。这里有个细节值得注意,前导通配符会导致索引失效,也就是LIKE '%数'这种写法没法走索引。数据量小的时候无所谓,但在生产环境,一个几千万行的表如果这么写,查询基本就是全表扫描。

另外,WHERE里如果有多组条件,MySQL一般会先根据索引和统计信息筛选出数据量最小的结果集,再执行后续过滤。理解这个执行顺序,可以帮助你写出更快的SQL,而不是盲目加条件。

3.2 聚合统计与GROUP BY:分组思维是关键

过了单表查询,很快就会遇到一类题:统计每门课程的平均分、统计每个学生的选课门数、统计各科成绩大于60分的人数。这类题的核心就是GROUP BY。

SELECT cid, COUNT(*) AS stu_cnt, AVG(score) AS avg_score FROM sc GROUP BY cid;

这行的意思是按课程ID把sc表分成若干组,然后对每组分别计算选课人数和平均分。理解GROUP BY一定要抓住一个点:分组之后,SELECT里的非聚合列必须出现在GROUP BY里,否则结果没有意义。比如你按cid分组,然后又想select sname,这时每个分组里可能有多个学生,数据库该显示哪一个?MySQL 8.0在ONLY_FULL_GROUP_BY模式下会直接报错。

WHERE和HAVING的执行顺序也在这里体现。WHERE是在分组之前过滤原始数据,HAVING是在分组之后过滤聚合结果。想筛选平均分大于80的课程,正确的逻辑是先按cid分组,再用HAVING avg(score) > 80过滤。如果写成WHERE avg(score) > 80,数据库在分组前压根不知道平均分是多少,直接报错。

3.3 多表连接:JOIN的三种形态要分清

等开始查“选修了数学课的学生名单”“选修了某课程且成绩大于90分的学生”,就要用多表连接了。最典型的三表关联:

SELECT s.sid, s.sname, c.cname, sc.score FROM student s JOIN sc ON s.sid = sc.sid JOIN course c ON c.cid = sc.cid WHERE c.cname = '数学';

这里的JOIN连接条件解决的是“表和表之间怎么对上”的问题,WHERE过滤条件解决的是“要哪些数据”的问题,两者各司其职。初学者常犯的错误是写完JOIN后,把所有条件一股脑塞进WHERE,这在结果上可能碰巧是对的,但可读性和性能都会受影响。

另一类经典题目是“查询没有选课的学生”,这类题能帮你真正理解LEFT JOIN。思路是用student表做主表,LEFT JOIN sc表,如果某学生在sc表里没有匹配记录,对应的sc.sid就是NULL。

SELECT s.sid, s.sname FROM student s LEFT JOIN sc ON s.sid = sc.sid WHERE sc.sid IS NULL;

有些同学会写成NOT IN (SELECT sid FROM sc),这在数据没有NULL时结果是对的,可一旦子查询结果集里有NULL,NOT IN会返回空集合,整个查询一条数据都查不出来。碰到这种情况,优先用NOT EXISTS或上面的LEFT JOIN写法,不容易踩坑。

3.4 子查询与相关子查询:筛选的艺术

子查询在50题里占的比例不低,常见的问法包括“查询比张三成绩高的同学”“查询每门课成绩超过平均分的学生”。

先看非相关子查询。它先执行内层查询,把结果给外层用来比较。比如“查询课程ID为1且成绩高于张三的学生”:

SELECT sid, score FROM sc WHERE cid = 1 AND score > ( SELECT score FROM sc WHERE sid = 1 AND cid = 1 );

注意,内层子查询和外层查询是两个独立的查询,子查询能拿到结果后,外层再用它做条件过滤。

相关子查询则完全不同。它的内层查询要引用外层查询的字段,每处理一行都要执行一次。比如“查询有任意一门课成绩大于90分的学生”,可以写成:

SELECT sname FROM student s WHERE EXISTS ( SELECT 1 FROM sc WHERE sc.sid = s.sid AND score > 90 );

这里EXISTS的作用是判断子查询是否返回了至少一行。从性能角度说,EXISTS和IN的大致区别是:外层表数据量小、内层表数据量大时,EXISTS通常更合适;反过来IN有时更好。但这只是经验法则,真实情况要结合执行计划来看。

3.5 排序、分页与去重:分组TopN的多种做法

排序、分页、去重几乎是所有SQL面试的必考点,50题里关于“每门课的前两名”就是最典型的代表。

如果你的MySQL是8.0,窗口函数是最直观的写法:

SELECT cid, sid, score FROM ( SELECT cid, sid, score, ROW_NUMBER() OVER (PARTITION BY cid ORDER BY score DESC) AS rn FROM sc ) t WHERE rn <= 2;

其中PARTITION BY cid表示按课程分组,ORDER BY score DESC表示组内按成绩降序,ROW_NUMBER()给每组内的每一行编号,最后只需要取编号小于等于2的即可。如果你需要“并列名次”,应该把ROW_NUMBER()换成RANK()或DENSE_RANK()。三者区别在于:ROW_NUMBER()严格按顺序编号1、2、3;RANK()遇到同分并列,且会跳号;DENSE_RANK()遇到同分并列,不跳号。实际业务里的排行榜功能,经常会用到RANK和DENSE_RANK,这个区别必须记清楚。

如果你的环境还是MySQL 5.7,没有窗口函数,可以用用户变量实现同样的效果:

SELECT cid, sid, score FROM ( SELECT cid, sid, score, IF(@prev_cid = cid, @rn := @rn + 1, @rn := 1) AS rn, @prev_cid := cid FROM sc, (SELECT @prev_cid := NULL, @rn := 0) vars ORDER BY cid, score DESC ) t WHERE rn <= 2;

这个写法理解起来稍微绕一点,核心原理是让每行按(cid, score DESC)排序,然后逐行比较课程编号是否变化,如果编号没变,行号自增;如果编号变了,行号重置为1。需要注意,用户变量在MySQL 5.7里的赋值顺序依赖执行计划,不能保证100%稳定,所以生产环境真的遇到这类TopN问题,我还是建议升级到8.0,或者改用自连接实现。

4. 那些容易翻车的SQL细节:避坑与排查实录

4.1 ONLY_FULL_GROUP_BY模式带来的烦恼

MySQL 5.7开始默认开启ONLY_FULL_GROUP_BY,MySQL 8.0也默认开启。在这种模式下,SELECT后面的非聚合字段必须出现在GROUP BY中,否则直接报错。

比如网上很多老版本的答案里有这样的写法:

SELECT sname, ssex, COUNT(*) FROM student GROUP BY ssex;

在MySQL 8.0里跑,大概率报错,因为sname没有出现在GROUP BY里。从逻辑上讲,这个报错是有道理的,按性别分组后,每组里可能有多名学生,到底取哪个sname结果是不确定的。MySQL宁可直接报错,也不给你一个随机结果。

遇到这个问题,正确做法是重写SQL,把需要展示的字段加进GROUP BY,或者用聚合函数包住它。如果真的只是临时想关闭这个模式,可以执行:

SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY,', ''));

但我不建议长期关闭。这个模式能在开发阶段就帮你发现不少逻辑问题,关闭之后短期省事,长期埋雷。

4.2 NULL值是真的坑

NULL在SQL里不是“空字符串”,也不是“0”,它代表“未知”。几乎所有对NULL的常规比较运算结果都不是TRUE或FALSE,而是UNKNOWN。这就带来了几个非常经典的坑。

第一个坑,WHERE score != 80查不出score为NULL的行。从业务逻辑上讲,成绩未知确实不能算“不等于80”,但很多人刚接触时都会在这上面栽跟头。解决办法是显式加上OR score IS NULL

第二个坑,COUNT(*)COUNT(score)结果不一样。COUNT(*)统计行数,COUNT(score)只统计score不为NULL的行数。如果某列允许NULL,这两个函数的结果就很可能对不上。

第三个坑,NOT IN遇到子查询含NULL,结果为空集合。这个前面已经说过,最好用NOT EXISTS替代。刷题时如果发现“怎么查不到数据”,第一反应就是检查涉及的表里有没有NULL值。

4.3 多表JOIN字段重名与别名

多表连接时,如果两张表都有同名列,SELECT里直接写列名会报Column 'sid' in field list is ambiguous。解决办法就是加表别名或表前缀,比如s.sidsc.sid。这个错误对新手很常见,面试官也经常故意把两张结构相似的表放一起,看你能不能意识到歧义问题。

用表别名还有一个好处,就是SQL写起来更短,尤其是表名很长时,比如order_detail起个别名od,整个语句看起来清爽很多。唯一要注意的是,一旦在FROM里定义了别名,后续的SELECT和WHERE里就得统一用别名,不能再写原始表名。

4.4 常见问题速查表

把我在练习和带人过程中遇到的典型问题整理成了一张表,方便你对照排查:

现象常见原因处理方式
插入中文变成乱码客户端字符集与表字符集不一致连接后执行SET NAMES utf8mb4,或修改客户端编码
分组查询报ONLY_FULL_GROUP_BY错误非聚合列不在GROUP BY中重写SQL,将字段加入GROUP BY,或用聚合函数包裹
NOT IN查询结果为空子查询结果含NULL改用NOT EXISTS或LEFT JOIN IS NULL
多表查询报ambiguous连接的多个表存在同名字段给表加别名,字段前加表前缀
查询结果中倒数第一名和正数第一混淆没注意排序方向确认ORDER BY是ASC还是DESC
数值字段求和有精度误差使用FLOAT或DOUBLE改用DECIMAL类型

4.5 UPDATE语法里的隐藏知识点

热搜词里有一条“mysql中更新子查询”,这虽然不属于50题的主线,但确实是个易错点。MySQL旧版本里,UPDATE语句的子查询不能直接引用目标表,比如你想把某学生的成绩改成这门课的平均分,直接写UPDATE sc SET score = (SELECT AVG(score) FROM sc WHERE cid = 1) WHERE sid = 1,在某些版本会报错“You can't specify target table 'sc' for update in FROM clause”。

解决办法通常是把子查询再包一层临时表,让MySQL认为它操作的不是目标表:

UPDATE sc SET score = (SELECT avg_score FROM ( SELECT AVG(score) AS avg_score FROM sc WHERE cid = 1 ) t) WHERE sid = 1;

这个知识点在面试中出现的频率不低,而且能很好地检验你写UPDATE语句时是否考虑到了MySQL的执行限制。

5. 从“会写”到“写好”:练习之外还能学什么

5.1 用EXPLAIN审视每一条SQL

刷题刷到一定程度,就不能只看结果对不对了,还要看SQL写得好不好。MySQL里最直接的手段就是给SQL加EXPLAIN前缀,查看执行计划。

拿“查询某学生的选课和成绩信息”来举例:

EXPLAIN SELECT s.sname, c.cname, sc.score FROM sc JOIN student s ON sc.sid = s.sid JOIN course c ON sc.cid = c.cid WHERE sc.sid = 1;

执行后会返回一张执行计划表,重点关注几列:type表示访问类型,从好到差大致是const、eq_ref、ref、range、index、ALL;key表示实际用到的索引,如果是NULL说明没有走索引;rows表示预估扫描行数,数字越小越好;Extra里如果出现Using filesort或Using temporary,通常说明这条SQL还有优化空间。

真正练习时,你可以故意在WHERE条件里写一个函数,比如WHERE YEAR(sage) = 1990,然后看执行计划,会发现即使sage字段有索引也不会被用到,这就是“不要在索引列上使用函数”的最直观证据。光看理论记不住,亲手实验一次就理解了。

5.2 索引基础:50题背后的索引思维

50题里的连接字段,天然就是需要加索引的字段。sc表上,sid和cid是复合主键,已经覆盖了大部分按学生查询和按课程查询的场景。如果你在自己的表上刷题,可以额外给高频查询字段建索引。

以“查询姓名重复的学生”这类题为例,如果student表数据量很大,sname字段上建了索引,GROUP BY sname HAVING COUNT(*) > 1的效率会高很多。反过来,如果sname没有索引,这个分组查询大概率会全表扫描加临时表,数据量一大就会很慢。

建索引要注意最左前缀原则,比如在(sid, cid)复合索引下,你按sid查能走索引,只按cid查反而走不了,除非在cid上单独建索引。这也是为什么我在建表时特意在sc表的cid字段上加了KEY idx_cid。很多生产事故,本质就是当初建表时没考虑好索引顺序,导致线上慢查询一堆。

5.3 面试变形题与真实场景映射

经典50题只是起点,刷完之后要会做场景映射。拿几道最常见的面试题来对照:

“每门课最高分”对应“每个部门最高工资”。解法思路完全一致,都是PARTITION BY按组划分,再在组内排序。

“查询没有选课的学生”对应“查询从未下单的用户”。LEFT JOIN加IS NULL,或者NOT EXISTS,思路一字不差。

“统计各科平均分”对应“统计各品类GMV”。换掉表名和字段名,SQL结构保持不变。

这种映射能力才是刷题真正的收获。你不必背下所有面试题,只要把50题里的基础模型理解透,遇到真实场景时把字段名替换一下,SQL自然就写出来了。

5.4 练习环境准备与进阶建议

练习这套题不需要多高配置的服务器,本地装一个MySQL客户端就够了。安装方式有很多种,Windows直接下载安装包,macOS用Homebrew,或者用Docker一条命令拉一个容器来练:

docker run --name mysql-50 -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -d mysql:8.0

用容器练习的好处是环境干净、随时可以删掉重建,不用怕把本机环境搞乱。连上数据库之后,把建表语句执行一遍,再插入数据,就可以开始刷题了。

50题刷完之后,如果想进一步巩固MySQL的操作能力,可以把一些重复出现的查询封装成存储过程。比如写一个接收课程ID参数、返回该课程成绩排名列表的存储过程,顺带把流程控制、游标、动态SQL都过一遍。这个进阶方向对应了热搜里“mysql存储过程”,确实值得花点时间,但前提是基础查询题已经刷透,不要本末倒置。

6. 最后再分享一点个人经验

我在不同阶段拿这套题反复验证过自己对MySQL的理解。第一遍刷题,我追求的是写得出来,能出正确结果就行;第二遍刷题,我开始注意看执行计划,思考为什么这条SQL走了全表扫描,那条走了索引;第三遍再刷,就想着怎么写才能让索引命中、怎么写更稳,面对同样一个问题时开始比较不同的解题方案。

如果你也是准备面试或者想把SQL基础打牢的人,我的建议是先自己把表结构和数据建一遍,再按题型分类去刷,最后用EXPLAIN回头审视每一条SQL。这套题真不用背答案,它值钱的地方在于让你把SQL思维刻进肌肉记忆。遇到不会的题别急着查,卡住半小时再去看答案,你会发现下一次看到类似问题,思路会清楚很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询