上两篇我们聊了怎么连接数据库、怎么查看表结构、怎么让 SELECT 把指定列的数据取出来,也把 WHERE 里的基础比较运算符过了一遍。这篇继续往查询这条线上走,重点解决三类连真实业务最常碰到的问题:结果怎么按想要的顺序出现、明细数据怎么变成汇总统计、几万行记录里怎么只拿自己需要的那一页。也就是说,把 MySQL 基本查询中的排序、去重、分组聚合、LIMIT 分页一次性说清楚。
如果你正在写 JavaWeb、管理后台报表、做数据分析前的数据探索,或者单纯面试前补 SQL 基础,这篇都适合。默认你用 MySQL 8.0。先提醒一句,刚入门时最容易踩的坑不是 SQL 写不写得出来,而是明明语法看着对,结果和自己心里预期完全不一样。之所以会这样,往往是对数据库执行查询的顺序没有概念。
1. 先把查询逻辑搞顺:数据库并不是从左往右读的
1.1 SELECT 只是“告诉数据库我想要什么”的一层外衣
很多人第一次看 SELECT 语句,会觉得这玩意儿就是从表里挑数据,没什么技术含量。从使用效果上说确实没毛病,但一旦你开始写多表关联、分组统计、嵌套子查询,就会发现一个根本矛盾:用户通常用中文描述业务需求,而 SQL 是一门对执行顺序有严格规定的语言。
比如“查每个班级的平均分,只要平均分不低于 60 分的,按平均分从高到低排,取前 5 个班级”。你脑子里首先想到的可能是“我要展示平均分”“要按平均分排序”,然后你会很自然地把 AVG(score)、ORDER BY 写在前面。但数据库拿到 SQL 以后,是先去找数据源,再过滤行,再分组,再筛选分组结果,最后才决定投影哪些列和怎么排序。
这就是为什么网上所有的 SQL 执行顺序图都把 FROM/JOIN 放在第一位。掌握这个顺序,不只是为了背面试题,更关键的是它能直接解释两个极其常见的问题:
- 为什么 WHERE 里不能写
COUNT(*) > 5这种条件?因为 WHERE 执行时还没分组,COUNT 根本还没算出来。 - 为什么 SELECT 里起的别名,在 WHERE 里用不了,但 ORDER BY 里往往能用?因为 WHERE 比 SELECT 先执行,而 ORDER BY 比 SELECT 晚执行。
一条多表查询的执行顺序大致可以这么理解:先确定数据来自哪些表,再通过 WHERE 把行过滤到最少,接着按分组字段把数据分堆,然后对每一堆执行聚合函数,再用 HAVING 过滤掉不符合条件的分组,最后才轮到 SELECT 计算表达式、去重、排序和分页。
1.2 用一个例子把执行顺序串起来看
看下面这条 SQL:
sql复制SELECT
class_no AS c,
COUNT(*) AS num
FROM student_score
WHERE course_name = '数学'
GROUP BY class_no
HAVING COUNT(*) >= 10
ORDER BY num DESC;
如果不了解执行顺序,很容易疑惑:num 这个别名在 SELECT 里刚定义,为什么 ORDER BY 能直接用?其实 MySQL 处理这条语句时,会先把 student_score 表找出来,用 course_name = '数学' 过滤掉不是数学成绩的行,然后按 class_no 分组,对每组统计个数,再用 HAVING COUNT(*) >= 10 把人数不到 10 的班级组扔出去。等这些事情做完以后,SELECT 才开始把 class_no 和统计结果拿出来并且补一个叫 num 的临时名字,排序阶段自然就能引用这个别名了。
所以我在带团队时要求新人先写 FROM 再写 WHERE 再写 GROUP BY,最后写 SELECT 和 ORDER BY。就算最后提交的代码把 SELECT 放在最前面,大脑里也要按照这个顺序去推导结果。养成这个习惯之后,很多所谓的“SQL 玄学问题”其实都是纸老虎。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. WHERE 过滤的隐藏玩法:别让逻辑运算符坑了你
2.1 AND 和 OR 混用时,括号永远不要省
上一篇文章里讲过简单的 WHERE 条件,比如 WHERE score >= 60。但真实系统中的过滤条件经常是并列的:查某个班级,同时还要看成绩段;查两个班级,还要排除某些课程。这时候一旦把 AND、OR 混在一起,优先级问题就会立刻冒出来。
MySQL 里的优先级规则是:NOT 最高,AND 次之,OR 最低。也就是 A OR B AND C 会被解析成 A OR (B AND C)。
实战现场模拟一下:
sql复制SELECT *
FROM student_score
WHERE class_no = '202501'
OR class_no = '202502'
AND score >= 60;
很多人的本意是:“我要查 202501 或 202502 这两个班,而且这两个班都要 60 分以上。”但上面这条 SQL 在数据库眼里是“查 202501 这个班的所有人,再加上 202502 班里 60 分以上的人”。这个差别,轻则报表多出几十条数据,重则线下排查半天。
正确的写法是用括号把 OR 两边先合并成一个整体:
sql复制SELECT *
FROM student_score
WHERE (class_no = '202501' OR class_no = '202502')
AND score >= 60;
写任何多条件 WHERE 时,我的建议是:不管优先级是否真的会有歧义,只要出现
