dense_rank()最适合并列名次场景,因其对相同值赋予相同名次且后续名次不跳号(如并列第1后为第2);rank()虽并列但跳号(1,1,3),row_number()则强制唯一编号(1,2,3),完全丢失并列语义。

为什么 DENSE_RANK() 比 RANK() 和 ROW_NUMBER() 更适合并列名次场景
当多个记录在排序字段上值相同时,DENSE_RANK() 会赋予它们相同名次,且后续名次不跳号——这是它“优雅”的核心:既保留并列语义,又维持名次序列连续。比如三个人并列第1,下一人就是第2;而 RANK() 会跳到第4,ROW_NUMBER() 则强制拆成1/2/3,完全丢失并列信息。
典型误用是拿 ROW_NUMBER() 做“排名展示”,结果业务方质疑:“为什么分数一样的人名次差两位?”——这不是数据问题,是函数选错了。
基本语法与必须注意的 ORDER BY 位置
DENSE_RANK() 是窗口函数,必须搭配 OVER() 子句,且 ORDER BY 必须写在 OVER 内部,不能放在外层查询末尾。写错位置会导致语法错误或逻辑错乱。
-
ORDER BY决定排名依据,支持多字段,如ORDER BY score DESC, create_time ASC(同分时按时间早者优先) - 不能在
OVER中使用WHERE或GROUP BY;过滤和分组需在外层完成 - 若需按部门分别排名,加
PARTITION BY dept_id,注意PARTITION BY必须在ORDER BY前
正确示例:
SELECT name, score, DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY score DESC) AS rank_in_dept<br>FROM employees;
常见陷阱:NULL 值、重复组合、性能抖动
DENSE_RANK() 默认把 NULL 当作最小值(ASC)或最大值(DESC),常导致排名偏移。例如 ORDER BY score DESC 时,NULL 会排在最末并统一得相同名次——如果业务要求忽略 NULL,得先用 WHERE score IS NOT NULL 过滤。
- 当
ORDER BY字段完全相同时(如所有 score 都为 95),整组记录将共享同一排名(如全是 1),这符合预期,但容易被误读为“没排上” - 大数据量下,未在
ORDER BY字段建索引会导致排序变慢;PARTITION BY+ORDER BY的复合场景建议建联合索引 - MySQL 8.0+、PostgreSQL、SQL Server、Oracle 均支持,但 SQLite 目前不支持窗口函数,强行使用会报
no such function: DENSE_RANK
和业务需求对齐:什么时候该用,什么时候不该用
真正需要“并列不跳号”的地方才用 DENSE_RANK()。比如学生成绩榜、销售业绩榜单、竞赛得分公示——这些场景中,“两人同分并列第3,下一位是第4”是合理且可解释的。
- 如果要严格区分所有行(如生成唯一流水号),必须用
ROW_NUMBER() - 如果要体现“累计落后人数”,比如“比你分数高的人有5个,所以你是第6”,那
RANK()更贴切 - 做分页时慎用
DENSE_RANK()做页码判断,因为同一名次可能跨多页;更稳妥的是用OFFSET/LIMIT或基于游标的分页
一个容易被忽略的细节:DENSE_RANK() 返回的是 BIGINT 或等效整型,但如果你把它和字符串字段拼接(如 CONCAT('Rank #', DENSE_RANK())),某些数据库(如旧版 MySQL)可能隐式转码失败,建议显式 CAST(... AS CHAR)。










