一、rank()函数简介
rank()是一个窗口函数,用于计算结果集中每一行的排名。它的基本语法如下:
rank() over ([partition by partition_expression] order by order_expression)
- partition by:可选子句,用于将结果集划分为多个分区,排名在每个分区内独立计算。
- order by:指定排名的顺序依据。
特点:
- 相同值的行会获得相同的排名。
- 下一个排名会跳过相同值的数量。例如,如果有两个第一名,下一个排名是第三名。
二、基础示例:部门内员工薪资排名
假设有一个employees表,包含员工姓名、部门和薪资信息。我们希望计算每个部门内员工的薪资排名。
示例数据
首先,创建示例数据:
with sample_data as (
select * from (
values
('alice', 'sales', 50000),
('bob', 'marketing', 55000),
('charlie', 'sales', 52000),
('david', 'it', 60000),
('eve', 'marketing', 55000),
('frank', 'it', 62000)
) as t(employee_name, department, salary)
)
排名查询
使用rank()函数按部门分区,按薪资降序排名:
select
employee_name,
department,
salary,
rank() over (partition by department order by salary desc) as dept_salary_rank
from
sample_data
order by
department, dept_salary_rank;
结果:
| employee_name | department | salary | dept_salary_rank |
|---|---|---|---|
| frank | it | 62000 | 1 |
| david | it | 60000 | 2 |
| bob | marketing | 55000 | 1 |
| eve | marketing | 55000 | 1 |
| charlie | sales | 52000 | 1 |
| alice | sales | 50000 | 2 |
解释:
- 在it部门,frank薪资最高,排名为1;david次之,排名为2。
- 在marketing部门,bob和eve薪资相同,均排名为1。
- 在sales部门,charlie薪资最高,排名为1;alice次之,排名为2。
三、高级应用示例
1. 每组top n记录
场景:找出每个类别中最贵的两个产品。
示例数据:
with products as (
select * from (
values
(1, 'a', 100),
(2, 'a', 80),
(3, 'b', 200),
(4, 'b', 180),
(5, 'b', 150),
(6, 'c', 120)
) as t(product_id, category, price)
)
查询:
select *
from (
select
product_id,
category,
price,
rank() over (partition by category order by price desc) as rank
from
products
) ranked
where rank <= 2;
结果:
| product_id | category | price | rank |
|---|---|---|---|
| 1 | a | 100 | 1 |
| 2 | a | 80 | 2 |
| 3 | b | 200 | 1 |
| 4 | b | 180 | 2 |
| 6 | c | 120 | 1 |
解释:
- 每个类别中,价格最高的前两个产品被筛选出来。
2. 百分位数计算
场景:计算每个学生的成绩百分位。
示例数据:
with scores as (
select * from (
values
('student 1', 85),
('student 2', 92),
('student 3', 78),
('student 4', 90),
('student 5', 88)
) as t(student, score)
)
查询:
select
student,
score,
rank() over (order by score) as rank,
round(100.0 * rank() over (order by score) / (select count(*) from scores), 2) as percentile
from
scores;
结果:
| student | score | rank | percentile |
|---|---|---|---|
| student 3 | 78 | 1 | 20.00 |
| student 1 | 85 | 2 | 40.00 |
| student 5 | 88 | 3 | 60.00 |
| student 4 | 90 | 4 | 80.00 |
| student 2 | 92 | 5 | 100.00 |
解释:
- 百分位数通过排名除以总记录数并乘以100计算得出。
四、rank()与其他窗口函数的比较
postgresql提供了多个窗口函数用于排名,各有特点:
| 函数 | 描述 |
|---|---|
| rank() | 相同值的行获得相同排名,下一个排名跳过相同值的数量。 |
| dense_rank() | 相同值的行获得相同排名,下一个排名不跳过,保持连续。 |
| row_number() | 每行分配唯一的序号,不考虑相同值,即使值相同也会分配不同序号。 |
示例:rank() vs dense_rank()
示例数据:
with scores as (
select * from (
values
('player 1', 100),
('player 2', 95),
('player 3', 95),
('player 4', 90)
) as t(player, score)
)

查询:
select
player,
score,
rank() over (order by score desc) as rank,
dense_rank() over (order by score desc) as dense_rank
from
scores;
结果:
| player | score | rank | dense_rank |
|---|---|---|---|
| player 1 | 100 | 1 | 1 |
| player 2 | 95 | 2 | 2 |
| player 3 | 95 | 2 | 2 |
| player 4 | 90 | 4 | 3 |
解释:
rank()在遇到相同分数时跳过了排名3。dense_rank()在遇到相同分数时不跳过排名,保持连续。
示例:row_number()
场景:为每日的销售记录分配唯一序号,按销售金额降序排列。
示例数据:
with sales as (
select
date '2023-01-01' as sale_date,
1000 as amount
union all
select
date '2023-01-01',
1500
union all
select
date '2023-01-02',
1200
union all
select
date '2023-01-02',
1200
)
查询:
select
sale_date,
amount,
row_number() over (partition by sale_date order by amount desc) as row_num
from
sales;
结果:
| sale_date | amount | row_num |
|---|---|---|
| 2023-01-01 | 1500 | 1 |
| 2023-01-01 | 1000 | 2 |
| 2023-01-02 | 1200 | 1 |
| 2023-01-02 | 1200 | 2 |
解释:
- 即使同一天有相同的销售金额,
row_number()也会为每条记录分配唯一的序号。
五、性能优化建议
使用窗口函数如rank()时,可能会对查询性能产生影响,尤其是在处理大数据集时。以下是一些优化建议:
- 使用partition by合理分区:将数据划分为较小的分区,可以减少每个窗口函数计算的数据量。
- 指定order by明确排序:确保
order by子句明确,避免全表排序带来的性能开销。 - 创建适当的索引:在
order by和partition by涉及的列上创建索引,可以加快排序和分区操作。 - 限制结果集:如果只需要前n条记录,结合
where rank <= n可以减少计算量。
六、总结
postgresql的rank()窗口函数是一个强大的工具,适用于各种排名需求,如部门内薪资排名、每组top n记录、百分位数计算等。通过合理使用rank()及其相关函数(如dense_rank()和row_number()),可以高效地处理复杂的数据分析任务。
关键点回顾:
rank()函数为相同值的行分配相同的排名,并跳过后续排名。- 结合
partition by和order by,可以实现多层次的排名需求。 - 与其他窗口函数(如
dense_rank()和row_number())相比,rank()在处理并列排名时有独特的行为。 - 通过优化查询和索引,可以提升窗口函数的性能表现。
希望本文的示例和解释能帮助你在实际项目中更好地应用rank()函数,提升数据处理的效率和准确性!
到此这篇关于postgresql中rank()窗口函数实用指南与示例的文章就介绍到这了,更多相关postgresql rank()窗口函数内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论