HAVING子句无法处理窗口函数生成的列?原因解析
为什么HAVING子句无法过滤窗口函数生成的别名?
核心原因:SQL的执行顺序
SQL语句的执行步骤是固定的,关键顺序如下:
- 先执行
FROM子句,加载表数据 - 接着处理
WHERE过滤行 - 如果有
GROUP BY则分组,计算聚合函数 - 然后执行
HAVING过滤分组结果 - 再执行
SELECT子句,包括计算窗口函数、生成列别名 - 最后是
ORDER BY和LIMIT
你第二段代码里的salary_rank是SELECT阶段才生成的窗口函数别名,而HAVING在SELECT之前就执行了,这时候salary_rank还没被创建,自然无法引用,所以报错。
为什么第一段代码能正常执行?
第一段里的HAVING departmentId = 1能跑,是因为MySQL的非标准扩展:当查询没有GROUP BY时,MySQL允许把HAVING当作WHERE来用,而departmentId是原表的列,在FROM阶段就已经存在,所以能被HAVING识别。但这种写法不符合SQL标准,不建议这么用,应该直接用WHERE departmentId = 1替代。
正确的写法:用子查询/CTE先计算窗口函数
要过滤窗口函数的结果,需要先把窗口函数的计算结果保存下来,再在外层查询过滤,比如:
方法1:子查询
SELECT * FROM ( SELECT departmentId, name, salary, DENSE_RANK() OVER (PARTITION BY departmentId ORDER BY salary DESC) AS salary_rank FROM Employee ) AS temp WHERE salary_rank = 1;
方法2:CTE(MySQL 8.0+支持)
WITH employee_ranked AS ( SELECT departmentId, name, salary, DENSE_RANK() OVER (PARTITION BY departmentId ORDER BY salary DESC) AS salary_rank FROM Employee ) SELECT * FROM employee_ranked WHERE salary_rank = 1;
这两种写法都是先让窗口函数在子查询/CTE里计算完成,生成salary_rank列,再在外层用WHERE过滤,符合SQL的执行逻辑。
内容的提问来源于stack exchange,提问作者张贺然
相关产品推荐
相关产品推荐

