← 返回题目列表

SQL 查询的逻辑执行顺序是什么?

高频 中等 第 9 / 28 题 更新于 2026/07/27
SQLSELECT执行顺序

简化版

SQL 查询的典型逻辑处理顺序是 FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT/OFFSET。这解释了为什么 WHERE 通常不能直接使用同层 SELECT 定义的别名,而 ORDER BY 通常可以。

详细版

SQL 的书写顺序不等于逻辑处理顺序:

书写:SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY → LIMIT
逻辑:FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT/OFFSET

各阶段先确定数据源和连接结果,再过滤行、分组、过滤分组,最后计算输出列并排序、截取结果。数据库真正的物理执行计划可以被优化器重排,例如选择不同连接顺序、下推谓词或使用索引,因此逻辑顺序是理解查询语义的模型,不是逐步执行的物理流水线。

完整版教学

一、先区分书写顺序和逻辑顺序

SQL 是声明式语言:开发者描述“想得到什么”,优化器决定“怎么得到”。逻辑处理顺序决定语句含义,物理执行计划则决定访问路径、连接算法和成本。

以查询部门平均工资为例:

SELECT department_id, AVG(salary) AS avg_salary
FROM employee
WHERE status = 'active'
GROUP BY department_id
HAVING AVG(salary) >= 10000
ORDER BY avg_salary DESC
LIMIT 5;

逻辑上先从 employee 取数据,用 WHERE 留下在职员工,再按部门分组,用 HAVING 留下平均工资达标的组,然后生成 SELECT 结果、排序并取前五行。

二、为什么 WHERE 不能使用 SELECT 别名

WHERE 处理时,同一查询层的 SELECT 列表尚未计算,因此下面的写法在 MySQL 等数据库中通常无效:

SELECT salary * 12 AS annual_salary
FROM employee
WHERE annual_salary > 200000;

应重复表达式,或者增加一层派生表/CTE:

SELECT *
FROM (
  SELECT salary * 12 AS annual_salary
  FROM employee
) AS e
WHERE annual_salary > 200000;

ORDER BY 位于 SELECT 之后,所以通常可以使用输出别名。不同数据库对别名解析有扩展规则,但不能把厂商扩展当成通用 SQL 规律。

三、WHERE 和 HAVING 为什么处在不同阶段

WHERE 过滤分组前的原始行,不能直接筛选本层聚合结果;HAVING 在分组后执行,可以使用聚合函数。

把能够在分组前判断的条件放到 WHERE,可以减少后续参与分组的数据量。只有依赖 COUNTSUMAVG 等分组结果的条件才需要放在 HAVING

四、窗口函数位于哪里

窗口函数在 WHEREGROUP BYHAVING 和普通聚合之后计算,通常只能直接出现在 SELECTORDER BY 中。要筛选 ROW_NUMBER() 的结果,需要再套一层查询:

SELECT *
FROM (
  SELECT e.*, ROW_NUMBER() OVER (
    PARTITION BY department_id ORDER BY salary DESC
  ) AS rn
  FROM employee AS e
) AS ranked
WHERE rn <= 3;

五、逻辑顺序不等于执行计划

优化器可以在不改变结果的前提下改写查询,例如把过滤条件尽早下推、把子查询转换为半连接、改变表的连接顺序。查看 EXPLAIN 得到的是物理执行策略,不能用执行计划中的节点顺序去否定 SQL 的逻辑处理顺序。

六、常见误区与追问

阶段已经可见的内容常见限制
WHEREFROM/JOIN 形成的行看不到 SELECT 别名和窗口结果
GROUP BY过滤后的输入行决定聚合粒度
HAVING分组和聚合结果可使用聚合条件
ORDER BY输出列与别名多数数据库允许引用别名
  • 误区:SQL 一定按书写顺序从 SELECT 开始执行。 逻辑处理顺序通常从 FROM/JOIN 建立输入关系开始,SELECT 是靠后的投影阶段。
  • 误区:WHERE 可以直接使用 SELECT 里的别名。 WHERE 逻辑上早于 SELECT,此时别名还没有产生;可把表达式放子查询、CTE 或改用可见阶段。
  • 误区:逻辑执行顺序就是物理执行计划。 优化器可以重排连接、下推谓词、选择索引;逻辑顺序解释语义,执行计划解释实际访问方式。
  • 追问:为什么 HAVING 能用聚合函数? 因为 HAVING 发生在 GROUP BY 和聚合计算之后,它过滤的是分组结果。
  • 追问:窗口函数为什么不能直接写在 WHERE? 窗口计算晚于 WHERE,要筛窗口结果通常需要包一层子查询或 CTE。
  • 追问:ORDER BY 为什么有时能用 SELECT 别名? ORDER BY 逻辑上靠后,投影别名已经形成,因此多数数据库允许它引用输出列别名。

记忆钩子:逻辑顺序解释“SQL 为什么这样写才有语义”,执行计划解释“数据库实际怎么把它跑快”。

七、加强记忆

先找表并连接,再用 WHERE 过滤行,然后 GROUP BY 分组、HAVING 过滤组,之后 SELECT 计算输出,最后 DISTINCT 去重、ORDER BY 排序并 LIMIT 截取。逻辑顺序解释语义和别名作用域,EXPLAIN 展示的则是优化器选择的物理执行方案。