不是写错了,这是mysql 8.0为窗口函数准备分区与排序上下文的正常行为:using filesort只发生一次,using temporary用于构建内存窗口框架且数据只进一次;而子查询中二者会重复触发,导致o(n²)复杂度。

EXPLAIN里总看到Using filesort和Using temporary,是不是写错了?
不是写错了,这是MySQL 8.0为窗口函数准备分区与排序上下文的正常行为。Using filesort只发生一次——全表或按索引顺序扫描一遍;Using temporary是构建内存中的窗口框架,数据只进一次。而关联子查询里这两个提示会重复出现,比如外层10万行,内层就可能触发10万次排序和临时表。
- 用
EXPLAIN ANALYZE看实际耗时:窗口函数的actual time通常集中在1–2个节点;子查询则分散在多个SELECT层级,且loops值常远大于1 - 如果
rows列数值异常大(比如比结果集大几十倍),先别急着调SQL,先检查是否缺索引 -
FORMAT=TREE输出中能看到明确的WINDOW节点,这是窗口执行路径的标志
为什么ORDER BY没走索引,但执行计划还是显示Using filesort?
窗口函数的排序成本压倒一切。即使你写了ORDER BY salary DESC,若没建INDEX(salary DESC)(MySQL 8.0+才支持降序索引),或者只建了INDEX(salary),优化器就无法跳过排序,只能走Using filesort。
-
PARTITION BY department ORDER BY salary DESC需要复合索引(department, salary DESC),字段顺序不能反 - 用表达式排序会失效,比如
ORDER BY DATE(created_at)——索引无法命中 - 时间字段重复很常见,
ORDER BY order_time不够稳,得补上唯一列如order_id保证确定性
窗口函数能用WHERE过滤吗?为什么加了就报错?
不能。RANK()、ROW_NUMBER()这类函数不能出现在WHERE子句里,因为它们是在SELECT阶段计算的,而WHERE在逻辑执行顺序上早于SELECT。强行写会导致语法错误或“Unknown column”提示。
- 正确做法是套一层子查询或CTE:
SELECT * FROM (SELECT ..., ROW_NUMBER() OVER (...) AS rn FROM t) AS t1 WHERE rn BETWEEN 100001 AND 100100 - 误在WHERE里写
RANK() OVER () > 10,MySQL直接拒绝解析 - 分页场景下,
PARTITION BY会破坏全局序号,导致ROW_NUMBER()变成每组从1开始,分页结果错乱
直方图对窗口函数执行计划有影响吗?
几乎没有。直方图只影响优化器对WHERE单列过滤条件的基数估算,不参与窗口排序、分区或函数计算的决策。它不会让ORDER BY突然走索引,也不会减少Using filesort次数。
- 如果慢查询主因是缺失索引或排序字段无有效索引,建直方图毫无意义
- 直方图对
JOIN条件列、GROUP BY列、OVER()里的PARTITION BY列都无效 - 确认该列分布严重倾斜、出现在WHERE中、且
HISTOGRAM字段为空,才考虑建——否则只是白忙活
真正卡住的地方往往不是函数本身,而是ORDER BY没走索引,或者误加了PARTITION BY把全局序号切碎了。跑之前先看EXPLAIN,别信“用了窗口函数就一定快”。











