RANK()遇并列跳号而DENSE_RANK()不跳,如分数95,95,90,85时分别得1,1,3,4和1,1,2,3;二者均须显式ORDER BY且PARTITION BY为分组排名所必需。

RANK() 和 DENSE_RANK() 处理并列时的编号逻辑不同
核心区别就一条:遇到相同排序值时,RANK() 跳过后续名次,DENSE_RANK() 不跳。比如分数为 95, 95, 90, 85,降序排下来:RANK() 给出 1, 1, 3, 4;DENSE_RANK() 给出 1, 1, 2, 3。这个差异不是“写法不同”,而是业务语义直接绑定——你要的是“第几名”(含空档),还是“第几档”(连续分层)。
常见误判是以为 DENSE_RANK() 会去重或合并行,其实它只是不跳号,重复值仍共享同一排名,行数完全不变。
两者都必须显式写 ORDER BY,否则报错
Window 'w' lacks an ORDER BY clause 这个错误很典型,说明你漏写了 ORDER BY。MySQL 8.0 不允许 RANK() OVER () 或 DENSE_RANK() OVER (PARTITION BY dept_id) 这类无排序的写法。
- 即使不分组,也得写
ORDER BY score DESC或ORDER BY id ASC(用主键保序) - 不能用
ORDER BY NULL,MySQL 8.0 已禁用该写法 - 若排序字段含
NULL,MySQL 默认按NULLS FIRST处理,可能把所有空值排最前并全标为1,需提前WHERE score IS NOT NULL或用COALESCE(score, -1)控制位置
PARTITION BY 漏写会导致全表统一排名,而非分组内排名
想查“每个部门薪资前三”,却只写 RANK() OVER (ORDER BY salary DESC),结果是全公司所有人混在一起排——PARTITION BY dept_id 不是可选项,是分组排名的必要条件。
正确结构必须是:RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC)
-
PARTITION BY定义切片边界,ORDER BY定义组内顺序,二者缺一不可 - 窗口函数不感知
GROUP BY,也不会自动按查询中的其他字段分组 - 如果业务要“全公司总榜 + 各部门子榜”,得用两个独立窗口函数,不能靠一个
OVER子句同时满足
不能出现在 WHERE、GROUP BY 或 HAVING 中
窗口函数只能用于 SELECT 列表或 HAVING(当搭配聚合时),写在 WHERE 里会报语法错误——因为计算发生在 WHERE 过滤之后、SELECT 投影之前。
想筛“部门内排名前3的员工”,不能写 WHERE RANK() OVER (...) ,得套一层子查询或 CTE:
SELECT * FROM (
SELECT dept_id, name, salary,
RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rk
FROM employees
) t WHERE t.rk <= 3;另外注意:RANK() 和 DENSE_RANK() 都不支持 ROWS BETWEEN 窗口帧子句,它们只依赖 ORDER BY 定义逻辑顺序,没有物理窗口范围控制能力。
实际用哪个,关键看业务是否容忍名次空档。发奖名单常选 DENSE_RANK()(Top 3 就是恰好三人或更多但名次连续),晋级名额常选 RANK()(两个第1名后,第3名才能进决赛)。别只看文档描述,拿真实数据跑一遍,看 NULL 怎么排、并列怎么断、边界值怎么卡,才是最可靠的验证方式。


















