窗口函数之排序函数详细解读及示例

窗口排序函数详细解读及示例

窗口排序函数是 SQL 中用于对窗口内的数据进行排序并赋予序号的一类函数。本文详细解读 ROW_NUMBER()RANK()DENSE_RANK()NTILE()PERCENT_RANK()CUME_DIST() 的用法、区别及实战示例。


📑 目录

  • [1. 窗口排序函数概述](#1. 窗口排序函数概述)
  • [2. ROW_NUMBER():唯一连续序号](#2. ROW_NUMBER():唯一连续序号)
  • [3. RANK():跳跃排名](#3. RANK():跳跃排名)
  • [4. DENSE_RANK():连续排名](#4. DENSE_RANK():连续排名)
  • [5. NTILE():分桶排名](#5. NTILE():分桶排名)
  • [6. PERCENT_RANK():相对百分比排名](#6. PERCENT_RANK():相对百分比排名)
  • [7. CUME_DIST():累积分布](#7. CUME_DIST():累积分布)
  • [8. 六大函数对比总结](#8. 六大函数对比总结)
  • [9. 综合实战示例](#9. 综合实战示例)

1. 窗口排序函数概述

窗口排序函数属于窗口函数的一种,其基本语法如下:

sql 复制代码
函数名() OVER (
    PARTITION BY 分组字段   -- 可选,指定分区
    ORDER BY 排序字段       -- 必选,指定排序规则
)
函数 返回值 特点
ROW_NUMBER() 从 1 开始的连续整数 每个分区内序号唯一且连续,不分并列
RANK() 跳跃排名 并列时序号相同,但下一个序号跳跃(如 1,1,3)
DENSE_RANK() 连续排名 并列时序号相同,下一个序号连续(如 1,1,2)
NTILE(n) 1 到 n 的桶号 将分区内数据尽可能均匀地分成 n 个桶
PERCENT_RANK() 0 到 1 之间 相对排名:(RANK-1) / (总行数-1)
CUME_DIST() 0 到 1 之间 累积分布:<=当前值的行数 / 总行数

2. ROW_NUMBER():唯一连续序号

含义

为每一行分配一个唯一的、连续的序号,从 1 开始。即使排序字段的值相同,也会随机(或根据其他列)赋予不同的序号。

语法

sql 复制代码
ROW_NUMBER() OVER (PARTITION BY col1 ORDER BY col2)

适用场景

  • 分组 TopN(如每个部门薪资前三)
  • 数据去重(保留每组第一条)
  • 生成行号

示例

表结构emp(emp_id, dept, salary)

emp_id dept salary
1 A 1000
2 A 2000
3 A 2000
4 B 1500

SQL

sql 复制代码
SELECT emp_id, dept, salary,
       ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM emp;

结果

emp_id dept salary rn
2 A 2000 1
3 A 2000 2
1 A 1000 3
4 B 1500 1

注意:A部门中薪资相同(2000)的两条记录分别获得了 1 和 2,没有并列。


3. RANK():跳跃排名

含义

排序后,相同值的行获得相同序号,但下一个不同值的序号会跳过中间的数量。例如:1, 1, 3, 4...

语法

sql 复制代码
RANK() OVER (PARTITION BY col1 ORDER BY col2)

适用场景

  • 需要处理并列且允许名次跳跃的排名(如比赛排名)
  • 财务或学术排名(如奖学金评定)

示例

SQL

sql 复制代码
SELECT emp_id, dept, salary,
       RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS rk
FROM emp;

结果

emp_id dept salary rk
2 A 2000 1
3 A 2000 1
1 A 1000 3
4 B 1500 1

注意:两个 2000 并列第 1,下一个行获得第 3 名(跳过了 2)。


4. DENSE_RANK():连续排名

含义

排序后,相同值的行获得相同序号,但下一个不同值的序号是连续的(不跳跃)。例如:1, 1, 2, 3...

语法

sql 复制代码
DENSE_RANK() OVER (PARTITION BY col1 ORDER BY col2)

适用场景

  • 需要处理并列但希望名次连续的场景(如业绩等级评定)
  • 压缩排名,不产生空洞

示例

SQL

sql 复制代码
SELECT emp_id, dept, salary,
       DENSE_RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS dr
FROM emp;

结果

emp_id dept salary dr
2 A 2000 1
3 A 2000 1
1 A 1000 2
4 B 1500 1

注意:两个 2000 并列第 1,下一个行获得第 2 名(连续)。


5. NTILE():分桶排名

含义

将分区内的数据尽可能均匀地分成 n 个桶(组),并为每行分配一个桶号(1 到 n)。若无法均匀分配,前若干桶会多一行。

语法

sql 复制代码
NTILE(n) OVER (PARTITION BY col1 ORDER BY col2)

适用场景

  • 数据分箱(如将用户按消费金额分成高、中、低三档)
  • 采样(取第 1 桶数据)
  • 并行处理任务划分

示例

SQL(将 A 部门 4 条数据分成 3 个桶):

sql 复制代码
SELECT emp_id, dept, salary,
       NTILE(3) OVER (PARTITION BY dept ORDER BY salary DESC) AS bucket
FROM emp;

结果

emp_id dept salary bucket
2 A 2000 1
3 A 2000 1
1 A 1000 2
(假设第4条) A 900 3

注意:4 行分 3 桶,桶 1 有 2 行,桶 2 和桶 3 各有 1 行。排序决定了行进入哪个桶。


6. PERCENT_RANK():相对百分比排名

含义

计算某行的相对排名位置,公式为:(RANK - 1) / (总行数 - 1)。返回值范围 [0, 1]。通常用来表示"超过百分之多少的行"。

语法

sql 复制代码
PERCENT_RANK() OVER (PARTITION BY col1 ORDER BY col2)

适用场景

  • 计算百分位数(如成绩超过 90% 的同学)
  • 数据分布分析

示例

SQL

sql 复制代码
SELECT emp_id, dept, salary,
       RANK() OVER w AS rk,
       PERCENT_RANK() OVER w AS pr
FROM emp
WINDOW w AS (PARTITION BY dept ORDER BY salary DESC);

结果(A 部门共 3 行):

emp_id dept salary rk pr
2 A 2000 1 (1-1)/(3-1)=0.0
3 A 2000 1 0.0
1 A 1000 3 (3-1)/(3-1)=1.0

公式中分母为 (总行数-1),所以最大值总是 1.0,最小值总是 0.0。


7. CUME_DIST():累积分布

含义

计算小于等于当前值的行数占总行数的比例。公式:<=当前值的行数 / 总行数。返回值范围 (0, 1]。

语法

sql 复制代码
CUME_DIST() OVER (PARTITION BY col1 ORDER BY col2)

适用场景

  • 计算经验累积分布函数(ECDF)
  • 判断某值在整个数据集中的"分位"

示例

SQL

sql 复制代码
SELECT emp_id, dept, salary,
       CUME_DIST() OVER (PARTITION BY dept ORDER BY salary DESC) AS cd
FROM emp;

结果(A 部门共 3 行):

emp_id dept salary cd
2 A 2000 2/3 ≈ 0.6667
3 A 2000 2/3 ≈ 0.6667
1 A 1000 3/3 = 1.0

解释:2000 有两行,小于等于 2000 的行数是 2,总行数 3,所以 cd = 2/3。1000 是最大值(降序排列下1000最小),所有行都 ≤ 1000,故 cd = 1。


8. 六大函数对比总结

函数 公式 相同值处理 序号连续性 值域
ROW_NUMBER() 不同序号,随机定序 连续 (1,2,3,4) 正整数
RANK() 相同序号 跳跃 (1,1,3,4) 正整数
DENSE_RANK() 相同序号 连续 (1,1,2,3) 正整数
NTILE(n) 可能分到不同桶 桶号连续 1...n
PERCENT_RANK() (RANK-1)/(count-1) 相同值得到相同值 连续浮点 [0,1]
CUME_DIST() <=当前值的行数/总行数 相同值得到相同值 连续浮点 (0,1]

快速记忆

  • ROW_NUMBER:唯一不重复,类似行号。
  • RANK:跳号,适合"竞赛排名"。
  • DENSE_RANK:不跳号,适合"等级评定"。
  • NTILE:分桶,等频划分。
  • PERCENT_RANK:相对排名(从0%到100%)。
  • CUME_DIST:累积占比(包含自身)。

9. 综合实战示例

场景 :员工绩效表 performance(emp_id, dept, score),要求:

  1. 每个部门按分数降序生成唯一序号(用于去重)。
  2. 生成有并列且不跳号的排名(用于发奖金档位)。
  3. 将每个部门员工按分数分为高、中、低三档(NTILE 3)。
  4. 计算每个员工分数在部门内的累积占比(CUME_DIST)。

SQL

sql 复制代码
SELECT emp_id, dept, score,
       ROW_NUMBER() OVER (PARTITION BY dept ORDER BY score DESC) AS row_num,
       DENSE_RANK()   OVER (PARTITION BY dept ORDER BY score DESC) AS dense_rk,
       NTILE(3)       OVER (PARTITION BY dept ORDER BY score DESC) AS tier,
       CUME_DIST()    OVER (PARTITION BY dept ORDER BY score DESC) AS cum_dist
FROM performance;

结果示例(假设某部门分数为 95, 95, 90, 85):

emp_id dept score row_num dense_rk tier cum_dist
1 A 95 1 1 1 0.5
2 A 95 2 1 1 0.5
3 A 90 3 2 2 0.75
4 A 85 4 3 3 1.0

解释:cum_dist 为 0.5 表示有 50% 的员工分数 ≤ 95(包括自己);NTILE(3) 将 4 行分 3 档,排序后前两行(95)进入 tier1,第三行(90)进入 tier2,第四行(85)进入 tier3。


以上详细解读了 Hive/Spark SQL 中常用的窗口排序函数。掌握它们的区别和适用场景,可以高效解决排名、分桶、分布分析等问题。

相关推荐
亚空间仓鼠1 小时前
关系型数据库MySQL(四):读写分离
android·数据库·mysql
武子康2 小时前
大数据-270 Spark MLib-机器学习库快速入门(分类/回归/聚类/推荐)
大数据·后端·spark
Wyz201210242 小时前
SQL中如何处理GROUP BY的不可排序问题_ORDERBY与聚合
jvm·数据库·python
Polar__Star2 小时前
jsoup如何读取html
jvm·数据库·python
亚空间仓鼠2 小时前
关系型数据库MySQL(三):主从复制
数据库·mysql
a9511416422 小时前
怎么防范通过phpMyAdmin上传WebShell_禁止into outfile权限
jvm·数据库·python
InfinteJustice2 小时前
如何统计SQL分组汇总数据_详解GROUP BY与HAVING用法
jvm·数据库·python
zhangchaoxies2 小时前
如何使用 AWS Lambda 和 Python 获取 EMR 集群的标签列表
jvm·数据库·python
数字化顾问2 小时前
(87页PPT)数据战略规划(附下载方式)
大数据·数据仓库·数据挖掘