窗口函数在select阶段才执行,where阶段无法访问;sql执行顺序为from→where→group by→having→select→order by;需用子查询或cte先生成rn列再过滤。

WHERE执行时窗口函数根本还没算出来
你写 WHERE rn = 1 报错,不是语法写错了,是数据库压根没生成 rn 这个值——WHERE 阶段连数据都没开始分组排序,更别说编号了。SQL 执行顺序固定为:FROM → WHERE → GROUP BY → HAVING → SELECT(这时 ROW_NUMBER() 才真正执行)→ ORDER BY。窗口函数只在 SELECT 阶段“出生”,WHERE 是它出生前就下班的同事。
必须用子查询或CTE把窗口结果“固化”成列
想过滤窗口结果,唯一可靠方式是先让它变成可被引用的列:
- 子查询必须带表别名(MySQL 强制要求,否则报
Every derived table must have its own alias) - 内层
SELECT中定义ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn - 外层
WHERE才能写t.rn ,不能漏掉 <code>PARTITION BY和ORDER BY,否则编号无意义 - CTE 更适合多步逻辑,比如先
RANK()再SUM() OVER (),中间结果可复用
别指望 CASE WHEN 或 FILTER 替代真实过滤
有人试 ROW_NUMBER() OVER (ORDER BY CASE WHEN status = 'active' THEN score END DESC),这只会让非 active 行排在 NULL 位置,ROW_NUMBER() 仍会给它们编号——序号不连续、不可控。真要排除这些行,必须提前在子查询里加 WHERE status = 'active'。PostgreSQL 的聚合 FILTER 语法对窗口函数无效,别混用。
QUALIFY 不是万能解,但值得知道
QUALIFY 是 BigQuery/Snowflake/DuckDB 等引擎提供的语法糖,语义等价于自动套一层子查询:SELECT ..., ROW_NUMBER() OVER (...) AS rn FROM t QUALIFY rn = 1。但它不通用:MySQL 8.0+ 和 PostgreSQL(截至 2026 年)原生不支持;且 QUALIFY 后必须至少有一个窗口表达式,不能只写普通条件。
ORDER BY 在 OVER() 里的作用——它不控制最终输出顺序,只决定窗口内行的相对位置。漏掉它,ROW_NUMBER() 编号就不可重现;写错它,比如用非唯一字段排序,同一组内多次执行可能得到不同结果。










