sql执行真正起点是from,它决定数据源和连接顺序,驱动表应选行数最少且有高效索引的表,on在join阶段生效,where在分组前过滤行,having在group by后过滤分组结果。

FROM 是 SQL 执行真正起点,不是 SELECT
MySQL 查询执行顺序和你写的顺序完全不一样——SELECT 看起来在最前面,但实际执行时它排第 6 步甚至更后。真正的起点是 FROM,它决定数据从哪来、怎么拼:先取基础表(驱动表),再按从右到左的顺序做笛卡尔积或 JOIN,生成第一个虚拟表 VT1。
- 如果
FROM后跟多个表,最后写的那个才是驱动表(例如FROM a JOIN b JOIN c中c最先被扫描) - 驱动表应选行数最少、有高效索引的表,否则中间结果集爆炸,后续所有步骤都变慢
-
ON条件在JOIN阶段就生效,而WHERE是等连接完才过滤——把本该写在ON的条件错放WHERE,LEFT JOIN 可能变成 INNER JOIN
WHERE 和 HAVING 容易混淆,但作用阶段完全不同
WHERE 过滤的是原始数据行,在分组前;HAVING 过滤的是分组后的聚合结果,在 GROUP BY 之后、SELECT 之前。很多人写 HAVING COUNT(*) > 1 却忘了加 GROUP BY,直接报错 Unknown column 'xxx' in 'having clause'。
-
WHERE不能用聚合函数(如COUNT()、AVG()),因为此时还没分组 -
HAVING可以用聚合函数,也能用SELECT中定义的别名(比如SELECT price AS p FROM t GROUP BY id HAVING p > 100) - 如果只想要满足聚合条件的分组,又不需要查原始明细,
HAVING比在应用层二次过滤更省带宽和内存
ORDER BY 和 LIMIT 不是“收尾动作”,而是资源大户
ORDER BY 生成的是游标(VC10),不是虚拟表;LIMIT 是在排序后截断,意味着 MySQL 得先把全部结果排好序,再扔掉不要的行。如果没走索引,ORDER BY 会触发 filesort,严重拖慢查询。
- 对大表分页(如
LIMIT 1000000, 20),即使有索引,MySQL 仍要扫描前 100 万行才能定位起点——改用基于游标的分页(WHERE id > ? ORDER BY id LIMIT 20)更稳 -
ORDER BY字段必须出现在SELECT列表中才能用于DISTINCT或GROUP BY的隐式排序逻辑(取决于 SQL mode) - 复合索引顺序必须匹配
ORDER BY字段顺序,且不能跳过前导列,否则索引失效
优化器介入早,但解析树和执行计划不是一回事
你写的 SQL 经过词法/语法分析生成解析树,再经预处理检查权限和语义(比如字段是否存在),最后交给查询优化器生成执行计划——这个计划不唯一,优化器会估算不同 JOIN 顺序、索引选择的成本,挑它认为最优的那个。
-
EXPLAIN FORMAT=TREE能看到优化器最终选了什么执行路径,比传统EXPLAIN更直观显示嵌套关系 - 强制走某个索引(
USE INDEX)或禁用某索引(IGNORE INDEX)有时比依赖优化器更可靠,尤其当统计信息过期时 - 子查询是否转成 JOIN、UNION 是否合并、常量表是否提前计算……这些都在优化器阶段决定,但不会反映在你的 SQL 文本里
真正难的不是记住顺序,而是理解每一步产生的中间结果形态:虚拟表没有物理存储,游标不可回溯,SELECT 别名在 WHERE 里根本看不到。这些细节一旦错位,查不出数据、报错、性能骤降,全在毫秒之间。











