必须加order by:所有排名类函数(row_number()、rank()、dense_rank()、ntile())在over()中强制要求,否则mysql报错;聚合类函数可不写但默认整分区计算,无法累计;partition by与group by目的相反,不可混用;rows框架需显式声明以确保累计或滑动计算准确。

窗口函数不是“能用就行”,而是必须明确分清 PARTITION BY、ORDER BY 和窗口框架三者的分工,否则结果会错得悄无声息。
什么时候必须加 ORDER BY?
所有排名类函数(ROW_NUMBER()、RANK()、DENSE_RANK()、NTILE())在 OVER() 中必须指定 ORDER BY,否则 MySQL 会报错 Window 'w' with order clause is not allowed。这不是可选项,是语法强制要求。
-
ORDER BY决定“谁排第几”,没它就无法定义排名逻辑 - 如果只写
PARTITION BY class不写ORDER BY score DESC,RANK() OVER(PARTITION BY class)直接执行失败 - 聚合类窗口函数(如
SUM() OVER())可以不写ORDER BY,但此时默认窗口是整分区所有行,且无法做累计计算
PARTITION BY 和 GROUP BY 别混用
PARTITION BY 是划分计算边界,GROUP BY 是聚合压缩行数——二者目的相反,不能互相替代。常见错误是把 GROUP BY 当成 PARTITION BY 的简写,结果查出空行或聚合后丢失明细。
- 想查“每个班的平均分,同时保留每个学生姓名和分数”,只能用
AVG(score) OVER(PARTITION BY class),不能写GROUP BY class - 如果误写成
SELECT name, class, AVG(score) FROM student_score GROUP BY class,结果只有 2 行(每班一行),张三、李四全没了 -
PARTITION BY后的数据仍保持原始 6 行,只是每行多了一列“本班平均分”
ROWS 框架决定累计/移动计算的范围
不做显式声明时,MySQL 默认窗口框架是 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW(逻辑值范围),但对时间或数字列容易出错;真正可控的是 ROWS(物理行偏移)。
- 算“截止当前行的累计分”:用
SUM(score) OVER(PARTITION BY class ORDER BY score DESC ROWS UNBOUNDED PRECEDING) - 算“最近 3 条记录的平均分”:用
AVG(score) OVER(ORDER BY id ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) - 漏写
ROWS或写错方向(比如2 FOLLOWING而非2 PRECEDING),结果会偏移或截断
LAG() 和 LEAD() 的默认值陷阱
LAG(event_time, 1) 在第一行返回 NULL,这本身没问题;但如果你用它算间隔(如 event_time - LAG(event_time, 1)),第一行就会变成 NULL,而后续行若没索引支撑,性能会陡降。
- 务必给
LAG()加第三个参数兜底,比如LAG(event_time, 1, '1970-01-01') OVER(PARTITION BY user_id ORDER BY event_time) - 在
user_events表上,user_id + event_time必须有联合索引,否则OVER(PARTITION BY user_id ORDER BY event_time)会全表扫描 -
LEAD()同理,最后一行默认返回NULL,直接参与减法或除法可能引发警告甚至隐式类型转换
实际跑通一条语句前,先盯住三件事:是否漏了 ORDER BY(尤其排名函数)、PARTITION BY 是否真对应业务分组粒度、ROWS 框架是否匹配计算意图——这三个地方任一出错,结果都不可信,且很难一眼看出来。











