dense_rank比rank更适合连续分组排名,因为它在遇到相同值时不跳过后续排名,确保组号连续不空缺,适用于按值聚类且需连续编号的场景,如分档归档或每n行一组。

为什么DENSE_RANK比RANK更适合连续分组排名
因为 DENSE_RANK 遇到相同值时不会跳过后续排名,而 RANK 会。比如三个人并列第1名,DENSE_RANK 给下一名排第2,RANK 却排第4——这在分组归档、取Top N连续区间时容易出错。
典型场景:按销售额分档(前10%为A档、次20%为B档…),或把用户按活跃度每50人划为一组,要求组号必须连续不跳空。
DENSE_RANK的基本写法和常见错误
核心是配合 ORDER BY 和可选的 PARTITION BY 使用;漏写 ORDER BY 会报错,这是最常踩的坑。
-
DENSE_RANK()必须带括号,即使里面没参数也不可省略 - 排序字段不能是别名(如
SELECT sales AS s, DENSE_RANK() OVER (ORDER BY s)...会报错),得用原始列名sales - 如果想按多字段排序(如先按部门、再按销售额),写成
ORDER BY dept, sales DESC,注意DESC要显式声明
SELECT name, dept, sales, DENSE_RANK() OVER (PARTITION BY dept ORDER BY sales DESC) AS rank_in_dept FROM sales_table;
如何用DENSE_RANK实现“每N行一组”的连续编号
直接对 DENSE_RANK 的结果做整除运算即可,但要注意起始值偏移——默认排名从1开始,而整除通常希望第1–5行为1组,需用 (DENSE_RANK() - 1) / N + 1。
- N = 5 时,
(DENSE_RANK() OVER (ORDER BY score DESC) - 1) / 5 + 1得到连续组号 1, 1, 1, 1, 1, 2, 2… - 数据库差异:PostgreSQL/Oracle 支持整数除法自动截断;MySQL 8.0+ 和 SQL Server 需用
FLOOR((rn - 1) / 5) + 1 - 若排序字段有大量重复值,
DENSE_RANK会让多行同属一组,这是预期行为;若要严格按物理顺序分组(无视值重复),得先加ROW_NUMBER()再计算
和NTILE对比:什么时候不该用DENSE_RANK
NTILE(N) 强制把结果分成 N 个大致等大的桶,但无法保证“连续性”——比如总行数不能被 N 整除时,前面几组会多一行,组号仍是 1 到 N,但各组大小不一致;而 DENSE_RANK 是纯按值排序生成逻辑序号,组大小完全由数据分布决定。
所以:需要「等量切分」用 NTILE;需要「按值聚类+连续编号」才用 DENSE_RANK。混用会导致组内差异大却强行塞进同一档,比如把销售额 100 万和 900 万的客户分到同一组。
真正难的是判断“相同值是否真该同组”——比如时间戳精确到秒,但业务上只看日期,就得先 DATE(created_at) 再排序,否则微小差异会让 DENSE_RANK 拆散本应同组的数据。











