rank()跳号体现名次断层,dense_rank()连续编号表达档位递进;前者适用于奥运奖牌榜等强调断层场景,后者适合客户分档、bi看板等需连续顺位的业务。

区别不在“对不对”,而在“想表达什么”:RANK() 用跳号体现并列造成的名次断层,DENSE_RANK() 用连续编号表达档位递进。选错函数不会报错,但业务语义会跑偏。
RANK() 跳号是设计,不是 bug
RANK() 把相同值看作一个“段”,段内共享名次,下一段名次 = 当前段名次 + 段长度。比如分数 [100, 100, 95, 90] → RANK() 结果是 [1, 1, 3, 4]。
- 常见错误现象:WHERE rnk = 2 查不到数据 —— 不是漏行,是业务上确实没有“唯一第二名”
- 适用场景:奥运奖牌榜、淘汰制绩效(A档→B档→C档,中间不能空档但允许多人同档)
- 性能无差别:底层都是排序后单次扫描,“跳”或“不跳”不增加开销
DENSE_RANK() 连续编号适合分档
DENSE_RANK() 只关心“这是第几个不同值”,值变就 +1,不管前面并列几行。同样分数 [100, 100, 95, 90] → DENSE_RANK() 是 [1, 1, 2, 3]。
- 常见错误现象:用 RANK() 做“取前三档”,结果漏掉大量本该在第 2 档的记录(因为
rnk = 2根本没出现) - 适用场景:客户分档(Top 5 档)、BI 看板、Excel 中的
RANK.EQ()对齐 - 注意:WHERE dense_rank
WHERE 里直接写 RANK() 或 DENSE_RANK() 一定失败
窗口函数在 SELECT 阶段才计算,而 WHERE 执行在之前。以下写法必然报错:
SELECT * FROM sales WHERE RANK() OVER (ORDER BY amount DESC) <p>报错信息通常是 <code>Invalid use of window function</code> 或字段不存在。</p>
- 正确做法必须套一层子查询或 CTE
- 漏掉这层嵌套是新手最高频的坑
- 如果还用了
PARTITION BY dept,却漏写PARTITION BY,会导致全表统一排序,业务逻辑彻底歪掉
MySQL 5.7 及更早版本根本不支持
在 MySQL 5.7 上执行含 OVER 的语句,会直接报错:ERROR 1064 (42000): You have an error in your SQL syntax。
- MySQL 8.0+、PostgreSQL、SQL Server 2017+、Oracle 支持
- SQLite 和旧版 MySQL 不支持窗口函数
- 不支持时硬用自连接或变量模拟,性能差、逻辑易错,不推荐
真正容易被忽略的点是:ORDER BY 字段有重复值(比如多人同分)又没加兜底字段(如主键 id 或时间戳 created_at),排名结果可能在不同执行中波动 —— 这不是函数问题,是排序本身不确定。











