WHERE必须在SELECT之前执行,因为数据库需先基于原始行数据筛选(删行),再对保留行投影列(选列);若颠倒顺序,将导致别名不可用、全量读取低效及逻辑矛盾。

WHERE 为什么必须在 SELECT 之前执行
因为数据库引擎需要先拿到“原始行数据”,才能决定哪些行该保留、哪些该丢弃;而 SELECT 是对已筛选/分组后的结果做“投影”(即挑字段、算表达式、起别名),它不参与数据行的存留判断。
换句话说:WHERE 是“删行”,SELECT 是“选列”。删行必须发生在选列之前,否则就可能出现“对着空结果集选列”或“用还没生成的别名去过滤”的逻辑矛盾。
常见错误:在 WHERE 中引用 SELECT 定义的别名
这是最典型的违反执行顺序导致的报错。例如:
SELECT name || '-' || dept_id AS full_name FROM emp WHERE full_name LIKE '%HR%';
这条语句在几乎所有主流数据库(MySQL、PostgreSQL、Oracle、SQL Server)中都会报错,比如:
ORA-00904: "FULL_NAME": invalid identifier(Oracle)Unknown column 'full_name' in 'where clause'(MySQL)
原因很直接:WHERE 执行时,full_name 这个别名根本还不存在——它要到 SELECT 阶段才被计算并命名。
修复方式只有两种:
- 把表达式原样复制进
WHERE:WHERE name || '-' || dept_id LIKE '%HR%' - 或者用子查询/CTE 把
SELECT结果先固化:SELECT * FROM (SELECT name || '-' || dept_id AS full_name FROM emp) t WHERE t.full_name LIKE '%HR%';
WHERE 早于 SELECT 对索引和性能的实际影响
这个顺序不是语法规定,而是物理执行效率的必然选择。数据库优化器会优先把 WHERE 条件下推到存储层:
- 如果
WHERE中有带索引的字段(如WHERE status = 'active'),引擎能直接跳过大量数据页,减少 I/O - 如果把过滤逻辑挪到
SELECT后(比如靠应用层 orHAVING),意味着所有行都得先读上来、再计算、再丢弃,白白浪费内存和 CPU -
GROUP BY和聚合函数也依赖WHERE先缩小数据集;100 万行分组 vs 1 万行分组,性能差距可能是数量级的
容易被忽略的边界点:HAVING 和 SELECT 别名的关系
HAVING 在 GROUP BY 之后、SELECT 之前执行,但它有时能“碰巧”用上 SELECT 中的别名——这不是标准行为,而是某些 MySQL 版本的扩展实现(如 5.7+)。不要依赖它。
真正可移植、可预期的方式是:
-
WHERE只能用原始表字段或表达式(不能用别名) -
HAVING只能用分组键或聚合函数(如COUNT(*)),理论上也不该用SELECT别名 - 如果逻辑复杂,优先用子查询或 CTE 显式控制执行层次,而不是赌优化器怎么解析
执行顺序不是为了让人背,而是帮你在写错时一眼看出问题出在哪——比如 WHERE 里写了别名,就说明你把“结果命名”和“数据筛选”两个阶段混在一起了。










