必须建联合索引(dept, score)才能让row_number() over (partition by dept order by score desc)高效执行,否则触发filesort导致全表排序;单列索引或字段顺序错误均无效,且窗口函数结果不可用于where过滤。

必须建联合索引,否则 ORDER BY 在窗口函数里会变成全表排序,慢到不可用。
为什么 ROW_NUMBER() OVER (PARTITION BY dept ORDER BY score DESC) 还是慢
不是语法问题,是执行计划没走索引。MySQL 窗口函数的 ORDER BY 需要真实排序,而如果没有对应索引,就会触发 Using filesort —— 数据量一过 10 万行,延迟就明显上升。
-
PARTITION BY dept ORDER BY score DESC要求索引顺序必须是(dept, score)或(dept, score DESC)(MySQL 8.0.13+ 支持降序索引) - 只建
(score)或(dept)单列索引无效;(score, dept)也不行——最左前缀不匹配分区字段 - 执行
EXPLAIN看输出里有没有Using filesort,有就是索引没生效 - 如果
score是表达式(比如ROUND(score)),得先建生成列再索引,不能直接在ORDER BY里算
如何避免“分组后只排前三却查出全部数据”
窗口函数计算发生在 SELECT 阶段,WHERE 和 HAVING 都早于它执行,所以不能直接写 WHERE rn 。
- 必须用 CTE 或子查询包裹:先算出
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY score DESC) AS rn,再外层WHERE rn - CTE 比子查询更易读,且 MySQL 8.0 对 CTE 的物化优化更好(尤其带
MATERIALIZED提示时) - 别在 CTE 外再加
ORDER BY——它不影响窗口内排序逻辑,但可能干扰优化器选择索引 - 如果只取每组 Top 1,且允许并列中任选一行,
ROW_NUMBER()比RANK()更合适,语义明确
ORDER BY 字段含 NULL 时排名错乱怎么办
MySQL 默认把 NULL 当作最小值(ASC 时排最前,DESC 时排最后),这会导致同组内 NULL 分数总被排在固定位置,掩盖真实业务意图。
- 显式控制:MySQL 8.0.22+ 支持
NULLS FIRST/NULLS LAST,例如ORDER BY score DESC NULLS LAST - 兼容旧版:用
COALESCE(score, -999999)或IFNULL(score, 0)替换,但注意别影响业务语义(比如 0 是有效分数) - 别用
ORDER BY score IS NULL, score DESC——虽然能控顺序,但会让索引失效 - 如果分组字段(如
dept)也可能为NULL,建议提前用COALESCE(dept, 'unknown')归一化,避免所有NULL dept被塞进同一组
真正卡住性能的往往不是函数本身,而是没意识到窗口函数对索引的强依赖——它不像普通 WHERE 条件能靠单列索引应付,PARTITION BY 和 ORDER BY 必须合起来建联合索引,少一个字段,就多一次磁盘排序。











