PostgreSQL RANK() 窗口函数实例详解

 更新时间:2026年06月24日 10:55:06   作者:倒流时光三十年  
在PostgreSQL中,RANK()是一个窗口函数,用于在结果集中为每一行分配一个排名,这篇文章给大家介绍PostgreSQL RANK()窗口函数完全解,感兴趣的朋友跟随小编一起看看吧

在PostgreSQL中,RANK() 是一个窗口函数,用于在结果集中为每一行分配一个排名。它与 ROW_NUMBER() 类似,但在处理具有相同值的行时有所不同。RANK() 会为所有具有相同值的行分配相同的排名,并跳过随后的排名(例如,如果有两个第一名,下一个排名将是第三名,而不是第二名)。

基本用法

RANK() 函数的基本语法如下:

RANK() OVER (ORDER BY column [ASC|DESC] [NULLS {FIRST|LAST}])

一、RANK() 是什么?(一句话解释)

“并列排名,但会跳号”

就像奥运会奖牌榜:如果两个人并列第一,就没有第二名,直接跳到第三名。

张三: 100分 → 第 1 名
李四: 100分 → 第 1 名  ← 并列
王五: 95分  → 第 3 名  ← 跳过第 2 名!
赵六: 90分  → 第 4 名

二、和 ROW_NUMBER() 的区别

函数相同值处理示例特点
ROW_NUMBER()强制不同1, 2, 3, 4即使分数相同,排名也不同
RANK()并列,跳号1, 1, 3, 4有并列时,后面的排名会跳过
DENSE_RANK()并列,不跳号1, 1, 2, 3紧凑排名,最符合直觉

直观对比:

SELECT 
    name,
    score,
    ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num,
    RANK() OVER (ORDER BY score DESC) AS rank,
    DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rank
FROM students;
-- 结果:
-- name  | score | row_num | rank | dense_rank
-- ------+-------+---------+------+------------
-- 张三  | 100   | 1       | 1    | 1
-- 李四  | 100   | 2       | 1    | 1      ← 并列第一
-- 王五  | 95    | 3       | 3    | 2      ← RANK 跳过 2
-- 赵六  | 90    | 4       | 4    | 3

三、8 个实用场景

场景 1:生成排行榜(允许并列)

需求: 销售排行榜,业绩相同的销售人员排名相同

SELECT 
    emp_name,
    sales_amount,
    RANK() OVER (ORDER BY sales_amount DESC) AS rank
FROM sales_performance
WHERE rank <= 10;  -- 前 10 名(可能超过 10 人,因为有并列)

为什么用 RANK 不用 ROW_NUMBER?

  • 如果用 ROW_NUMBER():两个业绩相同的人,一个排第 1,一个排第 2,不公平
  • RANK():两人都是第 1 名,公平合理

场景 2:找出所有并列第一名

需求: 找出考试成绩最高的所有学生(可能有多个)

SELECT * FROM (
    SELECT 
        student_id,
        name,
        score,
        RANK() OVER (ORDER BY score DESC) AS rank
    FROM exam_scores
) t
WHERE rank = 1;  -- 所有并列第一的学生

优势:MAX() + 子查询更简洁

场景 3:分段奖励(金银铜牌)

需求: 给前 3 名发金牌,4-10 名发银牌,11-50 名发铜牌

SELECT 
    emp_name,
    sales_amount,
    RANK() OVER (ORDER BY sales_amount DESC) AS rank,
    CASE 
        WHEN RANK() OVER (ORDER BY sales_amount DESC) <= 3 THEN '🥇 金牌'
        WHEN RANK() OVER (ORDER BY sales_amount DESC) <= 10 THEN '🥈 银牌'
        WHEN RANK() OVER (ORDER BY sales_amount DESC) <= 50 THEN ' 铜牌'
        ELSE '参与奖'
    END AS award
FROM sales_performance;

注意: 如果有并列,可能导致某个奖项人数超预期(比如 5 个人并列第 3,那金牌就有 5 个)。

场景 4:检测数据异常(跳号分析)

需求: 找出订单编号不连续的地方

SELECT 
    order_no,
    created_at,
    RANK() OVER (ORDER BY order_no) AS expected_no,
    order_no - RANK() OVER (ORDER BY order_no) AS gap
FROM orders
WHERE order_no - RANK() OVER (ORDER BY order_no) > 0;  -- 有缺口的地方

原理: 如果编号连续,order_no - RANK() 应该始终是同一个值;如果出现变化,说明中间有缺失。

场景 5:分组后取每组的前 N 名(含并列)

需求: 每个部门业绩最好的员工(包括并列)

SELECT * FROM (
    SELECT 
        dept_name,
        emp_name,
        sales_amount,
        RANK() OVER (PARTITION BY dept_name ORDER BY sales_amount DESC) AS rank
    FROM employees
) t
WHERE rank <= 3;  -- 每个部门前 3 名(含并列)

与 ROW_NUMBER 的区别:

  • ROW_NUMBER():每个部门严格 3 个人
  • RANK():如果第 3 名有并列,可能返回 4、5 个人

场景 6:计算累计占比

需求: 计算每个销售额在总销售额中的累计占比

SELECT 
    emp_name,
    sales_amount,
    SUM(sales_amount) OVER (ORDER BY sales_amount DESC) AS running_total,
    SUM(sales_amount) OVER () AS grand_total,
    ROUND(
        SUM(sales_amount) OVER (ORDER BY sales_amount DESC)::DECIMAL / 
        SUM(sales_amount) OVER () * 100, 
        2
    ) AS cumulative_percent
FROM sales_performance;

结合 RANK 使用:

-- 查看前 20% 的销售人员贡献了多少业绩
SELECT * FROM (
    SELECT 
        emp_name,
        sales_amount,
        RANK() OVER (ORDER BY sales_amount DESC) AS rank,
        COUNT(*) OVER () AS total_count
    FROM sales_performance
) t
WHERE rank::DECIMAL / total_count <= 0.2;  -- 前 20%

场景 7:去重但保留并列记录

需求: 删除重复订单,但如果金额和时间完全相同,保留所有

-- ❌ ROW_NUMBER 会只保留一条
DELETE FROM orders
WHERE id IN (
    SELECT id FROM (
        SELECT 
            id,
            order_no,
            ROW_NUMBER() OVER (PARTITION BY order_no ORDER BY id) AS rn
        FROM orders
    ) t
    WHERE rn > 1
);
-- ✅ RANK 会保留所有并列的
DELETE FROM orders
WHERE id IN (
    SELECT id FROM (
        SELECT 
            id,
            order_no,
            RANK() OVER (PARTITION BY order_no, amount, created_at ORDER BY id) AS rn
        FROM orders
    ) t
    WHERE rn > 1
);

场景 8:生成序号但允许空缺

需求: 给学生分配座位号,但如果有请假,座位号跳过

SELECT 
    student_id,
    name,
    status,
    CASE 
        WHEN status = '请假' THEN NULL
        ELSE RANK() OVER (
            PARTITION BY CASE WHEN status != '请假' THEN 1 ELSE 0 END 
            ORDER BY student_id
        )
    END AS seat_number
FROM students
ORDER BY student_id;

四、核心语法

RANK() OVER (
    PARTITION BY column1, column2  -- 可选:分组依据
    ORDER BY column3 DESC          -- 必填:排序规则
)

关键点:

  1. 不需要参数RANK() 括号里是空的
  2. 必须配合 OVER():声明这是窗口函数
  3. ORDER BY 必填:决定排名顺序
  4. PARTITION BY 可选:是否分组排名

五、性能优化

1. 避免重复计算

-- ❌ 慢:多次调用 RANK()
SELECT 
    emp_name,
    RANK() OVER (ORDER BY sales DESC) AS rank,
    CASE 
        WHEN RANK() OVER (ORDER BY sales DESC) <= 10 THEN '优秀'
        ELSE '普通'
    END AS level
FROM employees;
-- ✅ 快:用子查询或 CTE
WITH ranked AS (
    SELECT 
        emp_name,
        sales,
        RANK() OVER (ORDER BY sales DESC) AS rank
    FROM employees
)
SELECT 
    emp_name,
    rank,
    CASE WHEN rank <= 10 THEN '优秀' ELSE '普通' END AS level
FROM ranked;

2. 合理使用索引

-- 为 ORDER BY 字段创建索引
CREATE INDEX idx_sales_perf_sales ON sales_performance (sales_amount DESC);
-- 这样 RANK() 计算会更快
SELECT 
    emp_name,
    sales_amount,
    RANK() OVER (ORDER BY sales_amount DESC) AS rank
FROM sales_performance;

六、常见错误

错误 1:在 WHERE 中直接使用

-- ❌ 错误
SELECT * FROM employees
WHERE RANK() OVER (ORDER BY salary DESC) <= 10;
-- ✅ 正确:用子查询
SELECT * FROM (
    SELECT *, RANK() OVER (ORDER BY salary DESC) AS rank
    FROM employees
) t
WHERE rank <= 10;

错误 2:忘记 ORDER BY

-- ❌ 错误:RANK 必须有排序规则
RANK() OVER (PARTITION BY dept_id)
-- ✅ 正确
RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC)

错误 3:误解跳号逻辑

-- 假设有 3 个人并列第 1
SELECT 
    name,
    score,
    RANK() OVER (ORDER BY score DESC) AS rank
FROM students;
-- 结果:
-- name  | score | rank
-- ------+-------+------
-- 张三  | 100   | 1
-- 李四  | 100   | 1
-- 王五  | 100   | 1
-- 赵六  | 95    | 4    ← 不是 2!跳过了 2 和 3

七、RANK vs DENSE_RANK 选择指南

场景推荐函数原因
奥运会奖牌榜RANK()并列金牌,没有银牌
学生成绩排名DENSE_RANK()并列第一,下一个是第二
分页查询ROW_NUMBER()需要精确控制行数
前 N 名(含并列)RANK()确保不遗漏并列者
紧凑排名DENSE_RANK()排名连续,无空缺

八、记忆口诀

RANK 排名会跳号,并列之后空位跑
奥运奖牌最典型,金银铜牌不颠倒
若要排名更紧凑,DENSE_RANK 来效劳
分页去重用 ROW,场景不同选对宝

九、总结

核心要点

  1. RANK() = 并列排名,但会跳号(1, 1, 3, 4)
  2. 适用场景 = 排行榜、找并列第一、分段奖励
  3. 与 ROW_NUMBER 区别 = 相同值处理方式不同
  4. 与 DENSE_RANK 区别 = 是否跳号
  5. 使用时机 = 需要"公平排名"且允许空缺时

快速参考

-- 基本模板
SELECT * FROM (
    SELECT 
        字段列表,
        RANK() OVER (
            PARTITION BY 分组字段     -- 可选
            ORDER BY 排序字段 DESC    -- 必填
        ) AS rank
    FROM 表名
) t
WHERE rank <= N;  -- 前 N 名(含并列)

到此这篇关于PostgreSQL RANK() 窗口函数实例详解的文章就介绍到这了,更多相关PostgreSQL RANK() 窗口函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 详解PostgreSQL 实现定时任务的 4 种方法

    详解PostgreSQL 实现定时任务的 4 种方法

    这篇文章主要介绍了PostgreSQL 实现定时任务的 4 种方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • PostgreSQL如何修改默认端口号

    PostgreSQL如何修改默认端口号

    文章主要内容是关于在升级PostgreSQL过程中遇到的问题,以及解决这些问题的方法,升级过程中,由于端口号设置不正确,导致psql连接到旧版本的服务器,从而引发语法错误,解决方法是在连接数据库时指定正确的端口号,或者修改默认端口号
    2024-12-12
  • PostgreSQL中Slony-I同步复制部署教程

    PostgreSQL中Slony-I同步复制部署教程

    这篇文章主要给大家介绍了关于PostgreSQL中Slony-I同步复制部署的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用PostgreSQL具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-06-06
  • 修改postgresql存储目录的操作方式

    修改postgresql存储目录的操作方式

    这篇文章主要介绍了修改postgresql存储目录的操作方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • PostgreSQL自定义函数并且调用方式

    PostgreSQL自定义函数并且调用方式

    这篇文章主要介绍了PostgreSQL如何自定义函数并且调用,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-06-06
  • PostgreSQL存储过程循环调用方式

    PostgreSQL存储过程循环调用方式

    这篇文章主要介绍了PostgreSQL存储过程循环调用方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • PostgreSQL 查看数据库,索引,表,表空间大小的示例代码

    PostgreSQL 查看数据库,索引,表,表空间大小的示例代码

    PostgreSQL 提供了多个系统管理函数来查看表,索引,表空间及数据库的大小,下面详细介绍一下
    2013-08-08
  • PostgreSQL完成按月累加的操作

    PostgreSQL完成按月累加的操作

    这篇文章主要介绍了PostgreSQL完成按月累加的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • 基于postgresql数据库锁表问题的解决

    基于postgresql数据库锁表问题的解决

    这篇文章主要介绍了基于postgresql数据库锁表问题的解决,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • PostgreSQL生成JSON字符串的方法汇总

    PostgreSQL生成JSON字符串的方法汇总

    本文介绍了PostgreSQL中将数据转换为JSON字符串的方法,包括单行和多行数据的转换、复杂嵌套JSON的生成以及直接输出纯JSON字符串,所有函数均为PostgreSQL内置,无需安装扩展,需要的朋友可以参考下
    2026-05-05

最新评论