窗口函数必须出现在最外层SELECT或ORDER BY中,因SQL执行顺序为FROM→WHERE→GROUP BY→HAVING→SELECT(含窗口函数)→ORDER BY,子查询中若用于WHERE或外层GROUP BY则rn尚未生成,报Unknown column 'rn';正确做法是用CTE或带别名的派生表先固化结果。

不支持直接在嵌套查询的任意位置用窗口函数——LEAD、LAG、ROW_NUMBER 等必须出现在最外层 SELECT 或 ORDER BY 中,否则会报 Window function is not allowed in this context。
为什么子查询里写 ROW_NUMBER() 会报错?
SQL 执行顺序固定:FROM → WHERE → GROUP BY → HAVING → SELECT(含窗口函数)→ ORDER BY。窗口函数只在 SELECT 阶段计算,而子查询若被用在 WHERE 或外层 GROUP BY 的上下文中,此时 rn 还没生成,自然报 Unknown column 'rn'。
常见错误写法:
SELECT * FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn FROM orders) t WHERE rn = 1;
这在 MySQL 5.7 或 SQL Server 旧版本中直接语法报错;即使 MySQL 8.0+ 允许该语法,一旦外层加 GROUP BY 或 HAVING,仍大概率触发执行错误。
正确写法:用派生表或 CTE 固化窗口结果
必须让窗口计算结果变成“物理可见列”,再在外层引用。这是唯一兼容主流数据库(MySQL 8.0+、PostgreSQL、SQL Server 2012+)的方案。
- 派生表方式(必须带别名):
SELECT * FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC, id DESC) AS rn FROM orders WHERE status = 'paid') AS t WHERE t.rn = 1; - CTE 方式(语义更清晰,适合多步逻辑):
WITH ranked AS (SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC, id DESC) AS rn FROM orders WHERE status = 'paid') SELECT user_id, order_id, created_at FROM ranked WHERE rn = 1; - 别漏掉
ORDER BY中的唯一列(如id DESC),否则相同时间戳下ROW_NUMBER()分配不稳定 - MySQL 要求子查询必须用
AS t显式别名,否则报Every derived table must have its own alias
LEAD/LAG 在嵌套中怎么安全使用?
它们不能用于 WHERE 或 HAVING 条件过滤,也不能放在聚合子查询里。唯一安全路径是:先算偏移值,再在外层判断。
错误示例(直接用于过滤):WHERE LAG(amount) > amount → 报错,窗口函数不可用于 WHERE。
正确做法:
SELECT user_id, amount, prev_amount
FROM (
SELECT user_id, amount,
LAG(amount) OVER (PARTITION BY user_id ORDER BY date) AS prev_amount
FROM transactions
) t
WHERE amount < prev_amount;注意点:
-
LAG()结果默认为NULL(首行无前驱),需在WHERE中显式处理prev_amount IS NOT NULL,否则漏数据 - PostgreSQL 对
ORDER BY敏感:若子查询有ORDER BY而外层没重排序,LAG行序可能错位 - MySQL 8.0+ 支持在派生表中用
LAG,但若外层加GROUP BY,仍可能因上下文丢失分区信息而结果异常
真正容易被忽略的是执行计划层面的问题:哪怕语法全对,PARTITION BY user_id ORDER BY created_at 若缺乏联合索引 (user_id, created_at),MySQL 就得临时排序,EXPLAIN 里会出现 Using temporary; Using filesort——这时候窗口函数反而比老式自关联还慢。

















