简介:这份SQL语句练习题集面向MySQL初学者与希望巩固查询能力的开发者,通过单表与多表各四套题目,帮助解决从基础语法到复杂关联查询的实战训练需求。资源包共19个文件,以13个txt文本、5个sql脚本和1个zip压缩包为主,sql文件可直接导入MySQL环境执行,txt则承载题目说明与参考答案,整体仅22KB,轻量易用。内容覆盖SELECT、WHERE、ORDER BY、GROUP BY及聚合函数等单表操作,并延伸至INNER JOIN、LEFT JOIN、子查询等多表整合技巧,配套示例数据库便于边练边验证。目前已有918人学习下载,适合按套练习、对照答案复盘,逐步建立高效编写查询语句的思路,是数据库技能进阶的实用练习材料。
1. 到底什么是 SQL 练习题:为什么单表和多表要分开练
很多人第一次接触数据库时,都会遇到一个尴尬:语法看懂了,SELECT、WHERE、GROUP BY 都认识,可一旦面对一张真实的表,脑子里就一片空白,不知道该从哪写起。这就像学游泳,岸上把动作背得滚瓜烂熟,下水还是呛水。SQL 练习题就是那块让你真正下水的泳池——它把「看懂」逼成「写出来」。
而这份资源的核心,是把练习题分成了两类:单表题和多表题,各四套。单表题只在一张表里折腾,考的是过滤、排序、聚合、分组这些基本功;多表题要 JOIN 两张甚至更多表,考的是表与表之间怎么「对上号」。为什么非要分开?因为这两件事的思维难度完全不在一个层级。单表是「把一张表读明白」,多表是「把几张表拼成一张能读的表」。跳过单表直接练多表,就像还没学会走就想跑,JOIN 一写就晕。
搞懂这个区分,你才知道自己卡在哪一层,也才知道该练什么。
2. 单表练习到底在练什么:四个绕不开的基本功
2.1 过滤与排序:把「我要的那几行」挑出来
单表题里最基础的一类,就是给你一张表,让你按条件筛出特定的行,再按某个字段排个序。听起来简单,但初学者最容易在这里翻车。比如一张员工表,要你查出「工资高于 8000 且入职时间在 2020 年之后的员工,按工资从高到低排列」。这句话里藏着三个动作:先过滤、再过滤、最后排序。
-- 假设表名为 employees,字段有 name、salary、hire_date SELECT name, salary, hire_date FROM employees WHERE salary > 8000 AND hire_date >= '2020-01-01' ORDER BY salary DESC;这段代码的逻辑很直白:WHERE负责挑行,AND把两个条件同时满足的行留下,ORDER BY ... DESC让结果从大到小排。参数上要注意,日期在 SQL 里通常写成字符串形式'2020-01-01',不同数据库对日期格式的宽容度不一样,写标准格式最稳。
为什么这类题值得反复练?因为它训练的是「把自然语言翻译成条件表达式」的能力。真实工作里,产品经理说「我要最近三个月活跃、且消费超过五百的用户」,你脑子里要立刻拆成WHERE里的几个条件。这个翻译速度,就是靠单表题一遍遍磨出来的。
2.2 聚合与分组:从「一行行看」到「一类类算」
如果说过滤是逐行看,那聚合就是跳出来看整体。COUNT、SUM、AVG、MAX、MIN这五个聚合函数,是单表题的第二道坎。最典型的题目是「统计每个部门的平均工资」。这时候光用WHERE不够了,得用GROUP BY把行按部门分成几堆,再对每堆算平均值。
-- 按部门分组,算每个部门的平均工资和人数 SELECT department, AVG(salary) AS avg_salary, COUNT(*) AS headcount FROM employees GROUP BY department HAVING AVG(salary) > 7000;这里有个关键点:WHERE和HAVING的区别。WHERE在分组之前过滤行,HAVING在分组之后过滤组。上面这句的意思是「先按部门分组,算出每组的平均工资,再把平均工资超过 7000 的组留下」。如果你把HAVING AVG(salary) > 7000写成WHERE AVG(salary) > 7000,数据库会直接报错,因为分组还没发生,聚合值根本不存在。
这个坑几乎每个初学者都会踩一次。踩过之后,你就永远记住了:过滤「行」用WHERE,过滤「组」用HAVING。
2.3 空值处理:那个让结果悄悄少几行的隐形杀手
单表题里还有一个特别阴险的点:NULL。它不是 0,也不是空字符串,它代表「不知道」。而NULL参与任何比较运算,结果都是「未知」,不是真也不是假。这意味着WHERE column = NULL永远查不出任何东西,必须写成WHERE column IS NULL。
-- 错误写法:查不出任何结果 SELECT * FROM employees WHERE bonus = NULL; -- 正确写法 SELECT * FROM employees WHERE bonus IS NULL;更隐蔽的是聚合函数对NULL的处理。COUNT(*)会数所有行,但COUNT(bonus)只数bonus不为NULL的行。如果你要统计「有多少人有奖金」,用COUNT(*)就错了,得用COUNT(bonus)。这种细节在练习题里会反复出现,练多了你才会养成习惯:看到字段先想一句「这列会不会有 NULL」。
2.4 为什么单表要练四套:肌肉记忆靠重复
一套题练完,你可能只是「看懂了答案」。四套题练完,你才是「自己写出来了」。单表题的题型其实就那么几类:条件过滤、排序、聚合分组、空值处理、去重、限制行数。但每套题的组合方式不同,条件复杂度不同,练的就是你在不同组合下快速拆解的能力。
这就像打篮球练罚球,动作就一个,但要投几百次才能形成肌肉记忆。单表题就是 SQL 的罚球练习,枯燥但绕不过去。
3. 多表练习难在哪:JOIN 不是拼图,是找关系
3.1 从一张表到多张表:数据为什么要拆开
先想一个问题:为什么真实数据库不把所有信息塞进一张大表?比如订单和用户信息,如果放一起,每个订单都要重复存一遍用户的姓名、地址、电话,用户改个地址,几百条订单都得跟着改,改漏一条就数据不一致。所以要把用户信息单独放一张表,订单表里只存一个用户 ID,指向用户表里的那条记录。
这个「用 ID 指向另一张表」的设计,就是关系型数据库的核心。而多表查询,就是把这些拆开的数据重新拼回你需要的形状。拼的工具,就是JOIN。
3.2 INNER JOIN 与 LEFT JOIN:差一个字,差一批行
多表题里最容易混的,就是INNER JOIN和LEFT JOIN。用一句话区分:INNER JOIN只保留两张表都能对上的行,LEFT JOIN保留左表所有行,右表对不上的地方填NULL。
-- 场景:查所有用户及其订单金额,没有订单的用户也要显示 SELECT u.name, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id;如果这里把LEFT JOIN换成INNER JOIN,那些没下过单的用户就会从结果里消失。什么时候用哪个?看你的问题。如果问「哪些用户下过单」,用INNER JOIN;如果问「所有用户各自下了多少单,没下单的显示 0」,用LEFT JOIN配合COALESCE或IFNULL把NULL转成 0。
-- 没下单的用户,订单数显示 0 SELECT u.name, COUNT(o.id) AS order_count FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.name;注意这里用的是COUNT(o.id)而不是COUNT(*)。因为LEFT JOIN之后,没下单的用户那行,o.id是NULL,COUNT(o.id)不会数它,结果就是 0;而COUNT(*)会数这一行,结果是 1,那就错了。这个细节,就是多表题和聚合题交叉时的经典陷阱。
3.3 多表题的思维顺序:先想「要哪些行」,再想「怎么拼」
初学者写多表查询,最常见的错误是上来就写JOIN,写着写着发现结果多了或少了,再回头改。正确的顺序是反过来的:先明确最终结果要哪些行,再决定用哪种JOIN,最后才写连接条件。
举个例子:要查「每个部门的名称和该部门工资最高的员工姓名」。拆解步骤是:第一,结果要按部门展示,所以要以部门表为主;第二,每个部门要找到工资最高的员工,这需要在员工表里按部门分组求最大值;第三,把这两个结果拼起来。这时候可能需要子查询或者窗口函数,而不是简单的JOIN。
多表题练的就是这种「先想清楚再动手」的习惯。四套题下来,你会慢慢形成一套自己的拆解流程:看结果要什么、看数据在哪几张表、看表之间靠什么字段关联、看要不要分组、看要不要保留没匹配的行。
3.4 多表练习的常见翻车点:笛卡尔积和重复计数
多表查询有两个经典翻车点。第一个是忘记写连接条件,导致笛卡尔积——两张表每一行都和另一张表每一行组合,结果行数是两张表行数的乘积。一张 1000 行的表 JOIN 另一张 1000 行的表,没写条件就是 100 万行,查询直接卡死。
第二个是重复计数。当你 JOIN 了三张以上的表,如果其中一对关系是一对多,再 JOIN 另一个一对多,行数会膨胀,导致SUM或COUNT的结果偏大。这时候要么用子查询先聚合再 JOIN,要么用DISTINCT去重。这个坑在单表题里遇不到,只有多表题才会逼你面对。
4. 单表和多表的易混区别:别把两件事搅在一起
4.1 易混点一:WHERE 和 HAVING 到底谁先谁后
很多人知道WHERE过滤行、HAVING过滤组,但一到写题就放错位置。混淆的原因是两者都长得像「条件」,都跟在表名后面。区分方法很简单:看条件里有没有聚合函数。有SUM、AVG、COUNT的,用HAVING;没有的,用WHERE。而且WHERE永远写在GROUP BY前面,HAVING永远写在GROUP BY后面,顺序不能乱。
4.2 易混点二:ON 和 WHERE 在 LEFT JOIN 里的区别
这是多表题里最隐蔽的坑。在LEFT JOIN中,把条件写在ON里和写在WHERE里,结果可能完全不同。
-- 写法 A:条件在 ON 里 SELECT u.name, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id AND o.amount > 100; -- 写法 B:条件在 WHERE 里 SELECT u.name, o.amount FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE o.amount > 100;写法 A 会保留所有用户,只是订单金额大于 100 的才显示金额,其他的显示NULL。写法 B 会把没有大额订单的用户直接过滤掉,LEFT JOIN退化成了INNER JOIN。混淆的原因是两者都像「加个条件」,但ON是在拼表时决定怎么匹配,WHERE是在拼完之后决定留哪些行。
4.3 易混点三:COUNT(*) 和 COUNT(字段) 数的不一样
前面提过,COUNT(*)数所有行,COUNT(字段)只数该字段非NULL的行。混淆的原因是两者看起来都是「数数」。区分方法:问自己「这列有没有可能为 NULL」。如果有,而你要数的是「有值的行数」,就用COUNT(字段);如果要数「总行数」,就用COUNT(*)。在LEFT JOIN场景下,这个区别直接决定结果是 0 还是 1。
4.4 易混点四:INNER JOIN 和 LEFT JOIN 什么时候结果一样
有时候INNER JOIN和LEFT JOIN的结果看起来一模一样,这会让人误以为两者没区别。其实是因为右表的关联字段没有NULL,且左表每一行都能在右表找到匹配。一旦出现「左表有、右表没有」的行,两者立刻分道扬镳。区分方法:先问「左表有没有可能匹配不上右表」。有可能,就必须想清楚用哪种JOIN。
4.5 易混点五:子查询和 JOIN 是不是一回事
很多题既可以用子查询写,也可以用JOIN写,结果一样。这让人以为两者可以随便换。其实子查询更适合「先算出一个中间结果,再拿它当条件或数据源」,JOIN更适合「把两张表的列并排展示」。性能上,不同数据库的优化器对两者的处理不同,不能一概而论。区分方法:看你要的是「一个值/一列值」还是「多列拼在一起」。要值,用子查询;要拼列,用JOIN。
5. 什么时候该练、怎么练:一个过来人的边界判断
5.1 什么阶段适合练单表,什么阶段适合练多表
如果你刚学完SELECT的基本语法,能看懂WHERE和ORDER BY,那就从单表题开始。单表题的门槛低,反馈快,写错了立刻能看出结果不对。四套单表题练完,你应该能做到:看到一句需求,不用想太久就能写出对应的WHERE、GROUP BY、HAVING组合。
什么时候可以进多表?一个简单的判断标准:单表题里涉及聚合和分组的题,你能稳定写对,不再把WHERE和HAVING放错位置。这时候再上多表,你的注意力才能放在「表怎么关联」上,而不是同时还要纠结聚合怎么写。
5.2 练题的正确姿势:先自己写,再看答案,再重写
很多人练题的方式是「看题、想一下、看答案、哦懂了」。这个流程的问题在于,「看懂」和「写出」之间隔着一条河。正确的做法是:先自己写,哪怕写错也写完;然后对照答案,找出差在哪;最后把答案关掉,重新写一遍。第三遍才是真正把知识变成自己的。
如果一道题你看了答案觉得「原来如此」,但关掉答案又写不出来,那说明你还没会。这时候别急着做下一题,把这道题再写两遍。
5.3 什么时候别急着练多表
如果你连单表的GROUP BY都还写不利索,别急着上多表。多表题里经常需要「JOIN 之后再 GROUP BY」,两个难点叠在一起,很容易让人崩溃。先把单表的聚合练到不用想,再进多表,效率反而更高。
另外,如果你还没搞清楚NULL的行为,也先别碰多表。LEFT JOIN会产生大量NULL,如果你对NULL的比较和聚合行为不熟,结果错了你都不知道错在哪。
5.4 一个具体的练习节奏建议
假设你每天能抽出一小时练 SQL,可以这样安排:第一周,四套单表题,每套做两遍,重点练WHERE、ORDER BY、GROUP BY、HAVING的组合。第二周,四套多表题,每套做两遍,重点练INNER JOIN和LEFT JOIN的选择,以及 JOIN 之后聚合的写法。第三周,把八套题混着做一遍,不看题型提示,拿到题自己判断是单表还是多表思路。
这个节奏的关键是「重复」和「混合」。重复让你形成肌肉记忆,混合让你学会判断题型,而不是靠题目顺序猜解法。
5.5 我自己的一个习惯
从那以后我每次写多表查询,都强制自己先在本子上画一下:左表是什么,右表是什么,关联字段是哪个,要不要保留左表所有行。画完再写 SQL,翻车次数少了一大半。这个习惯看起来笨,但比对着屏幕空想快得多。
希望这份单表加多表的练习题单,能帮你把 SQL 从「看懂」练到「写出」。
本文还有配套的精品资源,点击获取