简介:这份SQL语句练习题资源面向MySQL初学者与希望巩固查询基础的开发者,围绕单表与多表两大主题各提供四套练习,帮助读者在动手实践中理解SELECT、WHERE、ORDER BY、GROUP BY及各类JOIN、子查询的用法。资源共19个文件,以13个txt文本与5个sql脚本为主,另含1个zip压缩包,整体约22KB,sql脚本可直接导入MySQL环境执行,txt则用于记录题目与答案,方便对照练习。内容覆盖单表过滤、排序、聚合统计,以及INNER JOIN、LEFT JOIN、RIGHT JOIN与嵌套子查询等核心场景,并配有示例数据库便于建库建表、插入数据后即时验证结果。目前已有918人学习下载,适合作为课堂练习、面试复习或日常查漏补缺的题库,通过反复演练可逐步提升编写高效查询与整合多数据源的能力。
1. 零基础学 SQL:为什么你该从练习题单开始上手
很多人第一次打开数据库教程,看到满屏的 SELECT、FROM、WHERE,脑子直接懵了。这玩意儿跟 Excel 到底有啥区别?我学它干嘛?其实你可以把 SQL 想成一种“跟数据对话的语言”——Excel 是你自己动手一行行翻、一列列筛,而 SQL 是你写一句话,让数据库帮你把想要的数据捞出来。数据量小的时候感觉不出来,一旦表里塞了几万行、几十万行,Excel 卡到转圈,SQL 一秒钟就给你结果。这就是为什么做运营、做产品、做开发、做数据分析,几乎都绕不开它。而这份“sql语句练习题单表+多表各四套”的资源,就是给零基础的人准备的一条练习路径:先拿单表练手感,再用多表练思维,每套题都围绕真实场景设计,让你不是背语法,而是用语法解决问题。
2. 先搞懂 SQL 到底在干什么:从一张表说起
2.1 数据库、表、行、列,用一张通讯录就能理解
别被“关系型数据库”“MySQL”这些词吓到。你手机里的通讯录就是一个活生生的表:每一行是一个联系人,每一列是姓名、电话、备注。数据库就是把这些表存起来、管起来的一个仓库,MySQL 是其中一种常见的仓库类型。SQL 则是你向这个仓库管理员提要求的语言。你说“把电话以 138 开头的联系人给我”,管理员就去翻表,把符合条件的行拎出来给你。你不需要知道它怎么翻的,只需要把要求说清楚。这就是 SQL 最核心的价值:描述你要什么,而不是指挥它怎么做。入门阶段,你只要记住四个最基本的动作——查(SELECT)、增(INSERT)、改(UPDATE)、删(DELETE),其中查用得最多,也最值得先练。
2.2 为什么单表练完必须练多表
单表查询就像在一个抽屉里找东西,条件写对就行。但真实世界的数据是拆开存的:用户信息在一张表,订单信息在另一张表,商品信息又在第三张表。你要回答“谁买了什么”这种问题,就得把多张表拼起来看。这个“拼”的动作,就是 JOIN。很多新手单表写得很顺,一遇到多表就翻车,根本原因是没理解“表与表之间靠什么字段关联”。比如用户表里有 user_id,订单表里也有 user_id,这个共同的字段就是桥梁。练习题单里专门拆出“单表四套”和“多表四套”,就是让你先把一个抽屉翻熟,再学怎么把几个抽屉对照着翻。这个顺序不能跳,跳了就容易在 JOIN 那里卡死。
2.3 第一个能跑的查询:从一张模拟用户表开始
下面这段代码,你可以直接复制到任何 MySQL 环境里跑。它建了一张极简的用户表,插了几条数据,然后查出了所有来自“北京”的用户。别怕,一行行看注释。
-- 创建一个模拟用户表,只保留最基础的字段 CREATE TABLE mock_users ( user_id INT PRIMARY KEY, -- 用户编号,主键,不能重复 user_name VARCHAR(50), -- 用户昵称 city VARCHAR(50), -- 所在城市 age INT -- 年龄 ); -- 插入四条模拟数据 INSERT INTO mock_users (user_id, user_name, city, age) VALUES (1, 'A同学', '北京', 22), (2, 'B同学', '上海', 25), (3, 'C同学', '北京', 28), (4, 'D同学', '广州', 24); -- 查询所有来自北京的用户 SELECT user_name, age FROM mock_users WHERE city = '北京';第一段 CREATE TABLE 是建一张空表,括号里定义了四个列,每个列后面跟的是数据类型:INT 存整数,VARCHAR(50) 存最多 50 个字符的文本。第二段 INSERT 是往表里塞数据,VALUES 后面按顺序对应列。第三段 SELECT 是查询,*表示所有列,但这里我指定了只要 user_name 和 age,WHERE 是筛选条件。跑完你会看到 A同学 和 C同学 两行。这就是单表查询的完整闭环:建表、插数据、查数据。练习题单里的单表四套,无非是在这个基础上换条件、换排序、换聚合,把 WHERE、ORDER BY、GROUP BY 这些逐个练熟。
3. 单表四套怎么练:把 WHERE 和聚合函数吃透
3.1 第一套练筛选:WHERE 后面能写多少种条件
单表练习的第一关,就是把 WHERE 玩熟。除了等于号,你还能用大于、小于、不等于、BETWEEN、IN、LIKE、IS NULL。比如“找出年龄在 23 到 27 之间的用户”写WHERE age BETWEEN 23 AND 27;“找出北京或上海的用户”写WHERE city IN ('北京', '上海');“找出名字里带‘同学’的”写WHERE user_name LIKE '%同学%'。这里有个新手常踩的坑:LIKE 里的%代表任意多个字符,_代表一个字符,别搞混。还有 NULL 的判断必须用IS NULL或IS NOT NULL,不能用= NULL,因为 NULL 在 SQL 里表示“未知”,跟任何值比较都返回未知,不是真也不是假。练习题单里会反复用这些条件组合,让你形成肌肉记忆。
3.2 第二套练排序和限制:ORDER BY 与 LIMIT 配合
查出来的数据顺序是乱的,你想按年龄从大到小排,就用ORDER BY age DESC,DESC 是降序,ASC 是升序(默认)。如果只想看前三条,加LIMIT 3。这两个经常一起用,比如“年龄最大的三个用户”就是ORDER BY age DESC LIMIT 3。注意一个细节:ORDER BY 可以跟多个列,比如ORDER BY city ASC, age DESC,意思是先按城市升序排,同一个城市里再按年龄降序排。LIMIT 还可以写成LIMIT 2, 3,表示跳过前 2 条,取接下来的 3 条,这个在分页场景里很常见。单表第二套就是专门练这个组合,让你能精确控制“要哪些行、按什么顺序、取多少条”。
3.3 第三套练聚合:COUNT、SUM、AVG、MAX、MIN
聚合函数是把多行数据“捏”成一个结果。比如SELECT COUNT(*) FROM mock_users返回总行数;SELECT AVG(age) FROM mock_users返回平均年龄;SELECT city, COUNT(*) FROM mock_users GROUP BY city返回每个城市各有多少人。这里的关键是 GROUP BY:它把数据按某个列分组,然后对每组做聚合。新手最容易犯的错是 SELECT 后面写了非聚合列,但没放进 GROUP BY,比如SELECT city, user_name, COUNT(*) FROM mock_users GROUP BY city,这在某些 SQL 模式下会报错,因为 user_name 没有分组依据。练习题单会逼你养成“SELECT 里的非聚合列必须出现在 GROUP BY 里”的习惯。另外 HAVING 是给分组后筛选用,WHERE 是分组前筛选,别搞反。
3.4 第四套练综合:把前面三套串起来
第四套通常是综合题,比如“找出每个城市年龄大于 23 的用户数量,按数量降序,只取前两名”。这种题拆开就是:WHERE 筛年龄、GROUP BY 按城市分组、COUNT 计数、ORDER BY 降序、LIMIT 取前二。写出来是这样:
SELECT city, COUNT(*) AS user_count FROM mock_users WHERE age > 23 GROUP BY city ORDER BY user_count DESC LIMIT 2;注意AS user_count是给聚合结果起个别名,方便后面 ORDER BY 引用。执行顺序上,SQL 先跑 FROM 找到表,再跑 WHERE 筛行,然后 GROUP BY 分组,接着 COUNT 聚合,再 ORDER BY 排序,最后 LIMIT 截取。这个顺序跟书写顺序不一样,但理解它对你排查错误很有帮助。单表四套练完,你基本能独立回答“这张表里符合某些条件的统计结果是什么”这类问题。
4. 多表四套怎么上手:JOIN 不是玄学,是搭桥
4.1 先理解“关联字段”这个桥梁
多表查询的核心就一句话:用两个表里都有的那个字段,把行对起来。比如用户表有 user_id,订单表也有 user_id,那么“用户 A 的订单”就是拿 A 的 user_id 去订单表里找相同 user_id 的行。这个 user_id 就是关联字段。没有关联字段,两张表就是孤岛,JOIN 也无从下手。练习题单里的多表题,第一件事就是让你找出关联字段。通常关联字段在一张表里是主键(唯一标识一行),在另一张表里是外键(指向主键的值)。你不需要背外键约束的语法,但要看懂“这个字段的值跟那个字段的值对应”。
4.2 INNER JOIN:只保留两边都匹配上的行
INNER JOIN 是最常用的连接方式。假设有一张模拟订单表:
CREATE TABLE mock_orders ( order_id INT PRIMARY KEY, user_id INT, product_name VARCHAR(50), amount DECIMAL(10,2) ); INSERT INTO mock_orders VALUES (101, 1, '键盘', 199.00), (102, 1, '鼠标', 89.00), (103, 3, '显示器', 899.00), (104, 5, '耳机', 299.00);现在要查“每个订单对应的用户名和商品名”:
SELECT u.user_name, o.product_name, o.amount FROM mock_users u INNER JOIN mock_orders o ON u.user_id = o.user_id;FROM mock_users u给表起了个别名 u,INNER JOIN mock_orders o给订单表起了别名 o,ON u.user_id = o.user_id就是搭桥条件。结果只会出现 user_id 在两张表里都能对上的行。注意订单表里 user_id 为 5 的那条,用户表里没有 5 号用户,所以不会出现在结果里。这就是 INNER JOIN 的特点:只保留匹配成功的。
4.3 LEFT JOIN:保留左表所有行,右表没匹配就填 NULL
如果你想看“所有用户,包括没下过单的”,就要用 LEFT JOIN:
SELECT u.user_name, o.product_name FROM mock_users u LEFT JOIN mock_orders o ON u.user_id = o.user_id;左表是 mock_users,它的每一行都会保留。如果某个用户在订单表里没有对应记录,product_name 就显示 NULL。这个在统计“哪些用户没下过单”时特别有用,加个WHERE o.order_id IS NULL就能筛出来。新手常犯的错是把 LEFT JOIN 的过滤条件写在 WHERE 里而不是 ON 里,导致左表行被意外过滤掉。比如你想看“用户及其金额大于 100 的订单”,条件o.amount > 100应该放在 ON 后面,放在 WHERE 里会把没匹配上的左表行也滤掉,LEFT JOIN 就退化成 INNER JOIN 了。
4.4 多表四套的递进节奏
多表四套通常这样安排:第一套练两个表的 INNER JOIN,第二套练 LEFT JOIN 和 NULL 判断,第三套练三个表串联(比如用户、订单、商品三张表),第四套练 JOIN 加聚合(比如“每个用户的订单总金额”)。三表串联时,JOIN 可以接着写:
SELECT u.user_name, p.category, SUM(o.amount) AS total FROM mock_users u INNER JOIN mock_orders o ON u.user_id = o.user_id INNER JOIN mock_products p ON o.product_id = p.product_id GROUP BY u.user_name, p.category;这里假设还有一张商品表,通过 product_id 关联。写多表 JOIN 时,建议一个 JOIN 一个 JOIN 地加,每加一个就跑一次看结果对不对,别一口气写完再调试,那样出错很难定位。这是血泪经验:多表查询的 bug 往往藏在某一个 ON 条件写错,分步验证能省你半小时。
5. 新手避坑:这五个错误几乎每个人都会犯
5.1 现象:查询报错“Unknown column”;原因:列名拼错或表别名用错;解决:逐列核对
新手写 SQL 经常把 user_name 写成 username,或者给表起了别名 u,但引用列时忘了加 u. 前缀。MySQL 报错会直接告诉你哪个列不认识,你回去看 SELECT 和 WHERE 里的列名,跟建表语句里的列名逐个对。如果用了别名,确认每个列前面都带了正确的别名前缀。别嫌麻烦,这是最常见的低级错误,检查一遍就能解决。
5.2 现象:GROUP BY 报错“not in GROUP BY”;原因:SELECT 里有非聚合列没分组;解决:补进 GROUP BY 或去掉
比如SELECT city, user_name, COUNT(*) FROM mock_users GROUP BY city,user_name 既不是聚合函数,也没在 GROUP BY 里,数据库不知道每个城市该显示哪个 user_name。要么把 user_name 加进 GROUP BY,要么从 SELECT 里删掉。入门阶段建议:SELECT 里除了聚合函数,其他列都放进 GROUP BY,养成习惯就不会错。
5.3 现象:LEFT JOIN 结果跟 INNER JOIN 一样;原因:WHERE 里过滤了右表字段;解决:把条件挪到 ON 后面
前面提过,LEFT JOIN 后如果在 WHERE 里写o.amount > 100,那些右表为 NULL 的行会被过滤掉,左表行就丢了。正确做法是把o.amount > 100写在 ON 子句里,这样左表所有行仍然保留,只是右表不满足条件的显示 NULL。这个坑非常隐蔽,因为 SQL 不报错,只是结果少了行,你不仔细对数据根本发现不了。
5.4 现象:NULL 判断失效;原因:用了= NULL或!= NULL;解决:改用 IS NULL / IS NOT NULL
NULL 在 SQL 里代表“未知”,任何值与 NULL 做等于比较,结果都是未知,不会返回真。所以WHERE city = NULL永远查不到东西。必须写WHERE city IS NULL。同理,判断非空用IS NOT NULL。这个规则适用于所有数据类型,别想着用<> NULL绕过去,绕不过的。
5.5 现象:聚合函数算出来不对;原因:WHERE 和 HAVING 用混;解决:分组前筛选用 WHERE,分组后筛选用 HAVING
比如“找出订单总金额大于 500 的用户”,总金额是聚合结果,不能用 WHERE 筛,必须用 HAVING:
SELECT user_id, SUM(amount) AS total FROM mock_orders GROUP BY user_id HAVING total > 500;WHERE 在 GROUP BY 之前执行,HAVING 在 GROUP BY 之后执行。搞清这个顺序,你就知道什么时候该用哪个。练习题单里会反复出现这两种筛选,做多了自然就分清了。
6. 下一步学什么:从练习题单走向真实场景
单表和多表各四套练完,你算是把 SQL 查询的骨架搭起来了。接下来别急着啃高级特性,先把这四套题反复做三遍:第一遍照着答案写,第二遍合上答案自己写,第三遍改条件自己出题自己写。这个过程能帮你把语法变成手感。然后你可以往三个方向走:一是学子查询,把一条查询的结果当成另一条查询的条件;二是学窗口函数,解决“组内排名”“累计求和”这类问题;三是学索引和 EXPLAIN,让你写的查询跑得更快。但这些都是后话,入门阶段最忌讳贪多。
我自己刚开始学的时候,总想一口气把 JOIN、子查询、窗口函数全看完,结果每个都似懂非懂,一写就错。后来老老实实把单表四套刷了三遍,多表四套刷了三遍,突然有一天写 JOIN 就不卡了。从那以后我每次学新东西都强制自己先找一套练习题单刷透,再去看原理。这个习惯帮我省了很多“以为自己会了”的幻觉时间。希望这份练习题单也能帮你把 SQL 从“看着发怵”变成“随手就写”。
本文还有配套的精品资源,点击获取