SQL查询语句的执行顺序到底是怎么样的?
2026/4/27 19:02:05 网站建设 项目流程

很多开发者写了多年SQL,却还是会被“为什么别名不能在 WHERE 里用,却能在 ORDER BY 里用?”这类问题绊住。其实,这背后的根本原因就是:SQL 的逻辑执行顺序和我们的书写顺序完全不同。今天我们就以 MySQL 为例,把查询语句的执行顺序彻底讲透。


一、书写顺序 vs 执行顺序

我们习惯的书写顺序(从键盘输入的顺序)通常是这样的:

sql

SELECT 字段列表 FROM 表名 JOIN 表名 ON 连接条件 WHERE 筛选条件 GROUP BY 分组字段 HAVING 分组后筛选 ORDER BY 排序字段 LIMIT 偏移量, 行数;

然而,数据库引擎理解并执行这条 SQL 的逻辑顺序却是:

text

1. FROM / JOIN 2. ON 3. WHERE 4. GROUP BY 5. HAVING 6. SELECT 7. DISTINCT 8. ORDER BY 9. LIMIT / OFFSET

这两个顺序的不一致,就是很多“怪现象”的根源。下面我们逐行拆解每一步到底做了什么。


二、逐步拆解逻辑执行过程

1. FROM / JOIN

首先处理FROM子句,将目标表(以及JOIN涉及的表)组装起来,形成初始数据集。
如果有多表联接,会通过笛卡尔积生成虚拟表VT1

2. ON

针对JOIN中的连接条件(即ON子句)进行筛选,只保留满足条件的行。
这时候会将那些没有匹配上的外连接行(比如LEFT JOIN中右表没有匹配的记录)以 NULL 填充,生成虚拟表VT2

3. WHERE

VT2应用WHERE条件,过滤掉不符合要求的行,得到VT3

⚠️ 注意:因为WHERE执行时,SELECT中的别名还没有被定义,所以WHERE里不能使用列别名。这就是WHERE column_alias = 'xxx'会报错的原因。

4. GROUP BY

根据GROUP BY指定的字段对VT3进行分组,生成VT4
在 MySQL 中,因为扩展语法的存在,你可以GROUP BY中使用SELECT里的别名(例如GROUP BY alias_name),但这不符合 SQL 标准,如果后续需要迁移数据库(如 PostgreSQL),这种写法就会失效,因此建议尽量少用。

5. HAVING

对分组后的结果VT4应用HAVING过滤条件,得到VT5
HAVING里通常配合聚合函数使用(比如HAVING COUNT(*) > 5)。

又一个常见的混淆点:标准 SQL 中 HAVING 也不能使用 SELECT 别名,因为 SELECT 还在后面。但 MySQL 允许这样做,可一旦使用,代码的可移植性就会变差。

6. SELECT

终于轮到SELECT!它从VT5中提取出你想要的列,并计算表达式、附上别名,输出虚拟表VT6
到这里,你在SELECT中定义的别名才正式“诞生”。

7. DISTINCT

如果查询中指定了DISTINCT,就会对VT6做去重操作,得到VT7

8. ORDER BY

VT7ORDER BY指定的规则进行排序,生成VT8
因为此时SELECT中的别名早已存在,所以ORDER BY 可以直接使用别名,写起来非常自然。

9. LIMIT / OFFSET

最后一步,从VT8中截取指定范围的行,返回最终结果集。


三、实战:用一个例子走通全流程

假设有两张表:

  • emp(员工表):id, name, dept_id, salary

  • dept(部门表):id, dept_name

需求:查询每个部门名称和平均工资,只保留平均工资大于 5000 的部门,按平均工资降序排列,取前 3 名。

sql

SELECT d.dept_name, AVG(e.salary) AS avg_sal FROM emp e JOIN dept d ON e.dept_id = d.id WHERE e.salary > 3000 GROUP BY d.dept_name HAVING avg_sal > 5000 ORDER BY avg_sal DESC LIMIT 3;

根据上面的逻辑顺序,这条 SQL 在 MySQL 中的执行流程如下:

  1. FROM + JOIN + ON
    empdept通过e.dept_id = d.id连接,生成VT2

  2. WHERE
    VT2中筛掉salary <= 3000的行,得到VT3

  3. GROUP BY
    d.dept_name分组,产生每组内员工的薪资集合,得到VT4

  4. HAVING
    过滤掉AVG(salary) <= 5000的组,留下平均薪资大于 5000 的部门,得到VT5
    这里HAVING avg_sal > 5000能正常运行,得益于 MySQL 的扩展;在其他数据库中可能直接报错。

  5. SELECT
    VT5中提取d.dept_name并计算AVG(e.salary)命名为avg_sal,形成VT6

  6. ORDER BY avg_sal DESC
    VT6avg_sal降序排序,别名可用,得到VT8

  7. LIMIT 3
    截取排序后的前 3 行返回。


如果你还在查询中使用了窗口函数(如ROW_NUMBER() OVER(...)),它的计算阶段处于HAVING 之后、ORDER BY 之前,所以窗口函数的结果不能在WHEREGROUP BYHAVING中直接引用。


四、常见误区三则

  1. WHERE 不能使用列别名
    因为WHERESELECT之前执行,那时候别名甚至还没出生,必须用原列名或表达式。

  2. MySQL 中 GROUP BY 和 HAVING 都可以用别名,但不要滥用
    虽然方便,却牺牲了 SQL 的可移植性,也可能混淆“逻辑执行顺序”这一核心认知。

  3. “执行顺序”不等于“优化器真实执行路径”
    以上是 SQL 的逻辑处理阶段。MySQL 优化器实际执行时可能会把WHERE条件下推到索引扫描中、改变 JOIN 顺序等,但这些优化保证最终结果与逻辑顺序等价,因此理解逻辑顺序对编写及调试 SQL 仍然至关重要。


五、总结

  • SQL 的逻辑执行顺序:FROM → ON → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT

  • 别名何时可用、何时不可用,全由这个顺序决定。

  • 掌握这一点,不仅让你能写出更精准的查询,还能帮助你在排查慢 SQL、阅读执行计划时思路更清晰。

希望这篇博客能让你以后面对 SQL 执行顺序问题时,心里始终有一张清晰的“路线图”。
如果觉得有帮助,欢迎点赞、收藏、转发,感谢支持!

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

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

立即咨询