DENSE_RANK()是窗口函数,只能在SELECT或ORDER BY中使用,不能在WHERE中——因WHERE执行早于窗口计算,排名尚未生成;必须用子查询或CTE先计算排名再过滤,推荐CTE写法。

为什么不能直接在WHERE里用DENSE_RANK()
因为 DENSE_RANK() 是窗口函数,只能出现在 SELECT 或 ORDER BY 子句中,不能在 WHERE 中使用——执行顺序上,WHERE 在窗口函数计算前就已过滤数据,此时排名根本还没生成。
必须用子查询或CTE套一层
把带 DENSE_RANK() 的结果先算出来,再对外层结果按排名过滤。这是唯一可靠的做法。
- 推荐用 CTE,逻辑清晰、可读性强:
WITH ranked AS ( SELECT *, DENSE_RANK() OVER (ORDER BY score DESC) AS drk FROM students ) SELECT * FROM ranked WHERE drk <= 3;
- 子查询也行,但嵌套深时容易漏括号或别名:
SELECT * FROM ( SELECT *, DENSE_RANK() OVER (ORDER BY score DESC) AS drk FROM students ) t WHERE drk <= 3;
- 注意:必须给窗口函数结果起别名(如
drk),否则外层无法引用
DENSE_RANK() 和 RANK()、ROW_NUMBER() 的区别直接影响“前三”的含义
如果存在并列数据,三者行为完全不同:
-
DENSE_RANK():并列不跳号,比如100,100,95,90→ 排名是1,1,2,3,取前三会返回4条记录 -
RANK():并列跳号,同上数据 → 排名是1,1,3,4,取RANK() 会漏掉 90 分那条 -
ROW_NUMBER():强制不并列,即使分数相同也会分出 1/2/3/4,可能把本该并列的高分者挤出前三
所以“前三”到底要不要包含并列者,得先明确业务规则,再选函数。
ORDER BY 里多个字段会影响排名稳定性
如果排序键不唯一(比如多人同分),DENSE_RANK() 的结果可能每次执行不一致——数据库不保证相同值内部的相对顺序。
- 加一个唯一字段兜底,比如
ORDER BY score DESC, id ASC,确保排名可复现 - 否则在分页或多次导出时,同分的人可能有时进前三、有时被踢出
- MySQL 8.0+、PostgreSQL、SQL Server 都支持多字段
ORDER BY,但 SQLite 目前(3.40+)仍不支持窗口函数中的多字段排序
实际用的时候,最常踩的坑不是语法写错,而是没想清楚“并列是否算同一档”以及“同分时靠什么决定先后”。这两个点定不下来,DENSE_RANK() 就只是个看起来整齐的数字。

















