dense_rank()与rank()关键区别在于并列时是否跳过名次:rank()跳过(如两个第1名后为第3名),dense_rank()不跳(紧接第2名);前者适用于需体现名次后移的场景(如奥运奖牌榜),后者用于要求名次连续的分组、分页或百分位计算。

为什么 DENSE_RANK() 和 RANK() 的结果看起来一样但实际不同?
关键区别在「跳过空位」:当有并列时,RANK() 会跳过后续名次(比如两个第1名后直接是第3名),而 DENSE_RANK() 不跳,紧接下一位(两个第1名后是第2名)。这在做连续分组、分页取 Top N 或计算百分位时直接影响逻辑正确性。
常见错误是把 DENSE_RANK() 当成 ROW_NUMBER() 用——后者根本不处理并列,纯按顺序编号,哪怕值相同也给不同序号。
- 并列场景必须用
DENSE_RANK()或RANK(),不能用ROW_NUMBER() - 需要名次连续(无空洞)→ 选
DENSE_RANK() - 需要体现“并列导致后面名次后移”的业务语义(如奥运奖牌榜)→ 选
RANK()
怎么写 DENSE_RANK() 的基本语法并避免 ORDER BY 错误?
DENSE_RANK() 必须配合 ORDER BY 使用,且只支持单个排序表达式(不能写 ORDER BY a ASC, b DESC 这种复合,但多数数据库其实支持——注意 PostgreSQL/SQL Server 支持,MySQL 8.0+ 也支持;老版本 MySQL 不支持,会报错 ERROR 3589 (HY000): Window function 'dense_rank' does not support an ORDER BY clause with multiple expressions)。
示例:按销售额降序排,并列者同名次
SELECT name, sales, DENSE_RANK() OVER (ORDER BY sales DESC) AS rank_num FROM sales_data;
- 漏写
ORDER BY→ 报错Window function requires an ORDER BY clause - 排序字段含 NULL → 默认排在最前(ASC)或最后(DESC),若需统一置后,加
NULLS LAST(PostgreSQL/Oracle)或用COALESCE(sales, -1)临时处理 - 想按多条件排序但数据库不支持 → 先用子查询生成组合键,再对外层调用
DENSE_RANK()
如何用 DENSE_RANK() 实现「每组内并列连续排名」?
加 PARTITION BY 就能分组计算。注意:分组字段值相等的行才参与同一组内的排序,跨组不比较。
一款AI工具,主要用于在主代理响应前,并行运行Kimi K2.5和GPT 5.3 Codex,注入双方观点以增强认知多样性,适合需要提升相关任务效率的用户。
比如按部门分组,查每个部门销售额前三的员工(允许并列,且第2名之后可能是第2名或第3名,但不会跳到第4名):
SELECT dept, name, sales, rank_num
FROM (
SELECT
dept, name, sales,
DENSE_RANK() OVER (PARTITION BY dept ORDER BY sales DESC) AS rank_num
FROM employees
) t
WHERE rank_num
-
PARTITION BY字段为空(NULL)→ 会被归为同一组,不是被忽略 - 分区字段类型不一致(如字符串混数字)→ 可能隐式转换失败,建议显式
CAST - 性能敏感场景:确保
PARTITION BY + ORDER BY字段上有联合索引,否则窗口函数可能触发全局排序,拖慢大表查询
DENSE_RANK() 在分页和 Top-N 场景中容易被忽略的边界问题
用 DENSE_RANK() 做分页时,无法直接靠 rank_num BETWEEN 11 AND 20 拿第2页(因为并列会导致某一页实际返回 12 行或 8 行)。它适合「取前 N 名」,不适合「严格每页 N 条」。
例如:要取总榜前 10 名(含并列),用 DENSE_RANK() 是对的;但要做「第2页,每页10条」,就必须先用 <code>ROW_NUMBER() 做全局唯一序号,再分页。
- 业务要求「显示名次+并列提示」(如“第1名(并列3人)”)→ 需额外统计每组名次的出现次数,不能只靠
DENSE_RANK() - 和
COUNT(*) OVER (PARTITION BY ...)联用时,注意窗口帧默认是UNBOUNDED PRECEDING TO CURRENT ROW,需显式写ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING才能统计全组 - MySQL 8.0 中
DENSE_RANK()不支持FRAME子句,但绝大多数使用场景也不需要它
真正麻烦的是名次需要参与后续计算(比如「排名前20%」),这时候得先算出总行数,再结合 DENSE_RANK() 值做比例判断——别直接用 DENSE_RANK() / COUNT(*),因为并列会让分子重复,比例失真。










