SQL窗口函数中的RANK和DENSE_RANK是处理分组排名需求的核心函数,二者都基于over子句定义的分组和排序规则生成排名,但在遇到相同值时的排名逻辑有所不同,适用于不同的业务场景。

RANK与DENSE_RANK基本语法
两个函数的语法结构完全一致,都需要配合OVER()子句使用,OVER()中通过PARTITION BY指定分组字段,通过ORDER BY指定排序字段,语法如下:
-- RANK函数语法 RANK() OVER (PARTITION BY 分组字段 ORDER BY 排序字段 [ASC|DESC]) -- DENSE_RANK函数语法 DENSE_RANK() OVER (PARTITION BY 分组字段 ORDER BY 排序字段 [ASC|DESC])
分组排名实现示例
假设我们有一张学生成绩表student_score,包含学生ID、班级ID、学科、成绩四个字段,现在需要按班级分组,对每个班级内的学生成绩进行降序排名,分别使用RANK和DENSE_RANK实现。
首先创建测试表并插入测试数据:
-- 创建学生成绩表
CREATE TABLE student_score (
student_id INT,
class_id INT,
subject VARCHAR(20),
score INT
);
-- 插入测试数据
INSERT INTO student_score VALUES
(1, 1, '数学', 95),
(2, 1, '数学', 95),
(3, 1, '数学', 90),
(4, 1, '数学', 88),
(5, 2, '数学', 92),
(6, 2, '数学', 92),
(7, 2, '数学', 85);
使用RANK函数实现分组排名
执行以下SQL语句,按班级分组,数学成绩降序排名:
SELECT
student_id,
class_id,
score,
RANK() OVER (PARTITION BY class_id ORDER BY score DESC) AS rank_num
FROM student_score
WHERE subject = '数学';
执行结果如下:
| student_id | class_id | score | rank_num |
|---|---|---|---|
| 1 | 1 | 95 | 1 |
| 2 | 1 | 95 | 1 |
| 3 | 1 | 90 | 3 |
| 4 | 1 | 88 | 4 |
| 5 | 2 | 92 | 1 |
| 6 | 2 | 92 | 1 |
| 7 | 2 | 85 | 3 |
使用DENSE_RANK函数实现分组排名
执行以下SQL语句,同样按班级分组,数学成绩降序排名:
SELECT
student_id,
class_id,
score,
DENSE_RANK() OVER (PARTITION BY class_id ORDER BY score DESC) AS dense_rank_num
FROM student_score
WHERE subject = '数学';
执行结果如下:
| student_id | class_id | score | dense_rank_num |
|---|---|---|---|
| 1 | 1 | 95 | 1 |
| 2 | 1 | 95 | 1 |
| 3 | 1 | 90 | 2 |
| 4 | 1 | 88 | 3 |
| 5 | 2 | 92 | 1 |
| 6 | 2 | 92 | 1 |
| 7 | 2 | 85 | 2 |
二者核心差异对比
通过上面的示例可以清晰看到两个函数的排名规则差异:
- RANK函数:遇到相同值时排名相同,下一个不同值的排名会跳过重复的排名数量,比如两个第1名之后,下一个排名是第3名。
- DENSE_RANK函数:遇到相同值时排名相同,下一个不同值的排名不会跳过,比如两个第1名之后,下一个排名是第2名。
适用场景说明
在实际业务中,需要根据需求选择合适的函数:
- 如果需要统计竞赛排名,允许并列后跳过名次,比如两个第一名之后没有第二名,直接是第三名,就使用RANK函数。
- 如果需要统计等级排名,比如成绩分为A、B、C三个等级,相同分数属于同一等级,下一个等级连续不跳过,就使用DENSE_RANK函数。
注意:如果不需要分组,只需要全局排序排名,可以省略OVER()子句中的PARTITION BY部分,只保留ORDER BY即可。
SQL窗口函数RANKDENSE_RANK分组排名over子句修改时间:2026-07-07 15:15:33