惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - 小纸条

ruoyiai 启动指南 反向传播 numpy的使用 B 和 B+树 红黑树 ruoyi-vue 梯度下降法 博弈论 离散化 AcWing 907. 区间覆盖 AcWing 906. 区间分组 AcWing 908 最大不相交区间数量 AcWing 905. 区间选点 AcWing 104. 货仓选址 动态规划经典题 1226. 哲学家进餐 1195. 交替打印字符串 1117. H2O 生成 1116. 打印零与奇偶数 关联子查询
窗口函数
小纸条 · 2025-11-02 · via 博客园 - 小纸条

🪄 一、窗口函数简介

窗口函数(Window Function)用于对查询结果集中的每一行计算“相对排名”或“顺序编号”。

三大常用函数:

ROW_NUMBER() 为每行生成唯一连续编号 不并列 不跳号 1, 2, 3, 4, ... RANK() 并列时相同排名 并列 跳号 1, 2, 2, 4, ... DENSE_RANK() 并列时相同排名 并列 不跳号 1, 2, 2, 3, ...
函数功能描述相同值处理是否跳号示例序号

🧱 二、示例表

CREATE TABLE scores (
    student_name VARCHAR(10),
    score INT
);

INSERT INTO scores VALUES
('张三', 95),
('李四', 92),
('王五', 92),
('赵六', 88),
('孙七', 85);

💻 三、使用窗口函数实现排名

SELECT
    student_name,
    score,
    ROW_NUMBER() OVER (ORDER BY score DESC) AS rn,
    RANK()       OVER (ORDER BY score DESC) AS rk,
    DENSE_RANK() OVER (ORDER BY score DESC) AS dr
FROM scores;

📊 查询结果

张三 95 1 1 1 李四 92 2 2 2 王五 92 3 2 2 赵六 88 4 4 3 孙七 85 5 5 4
student_namescoreROW_NUMBERRANKDENSE_RANK

🧠 四、区别详解

ROW_NUMBER() 不并列 连续 每行唯一编号,即使分数相同 RANK() 并列 跳号 类似比赛排名(两个第二名后直接第四名) DENSE_RANK() 并列 不跳号 等级划分更紧密(1,2,2,3,4)
函数并列情况下一名解释

⚙️ 五、在不支持窗口函数的数据库中用自联结实现

有些旧版本数据库(如 MySQL 5.x)不支持窗口函数,我们可以通过自联结 (Self Join) 实现相同逻辑。

🧩 1️⃣ 模拟 RANK()

思路:
计算比当前行分数更高的行数(包括当前行自己)
→ 直接用 >= 替代 > + 1

SELECT 
    a.student_name,
    a.score,
    COUNT(b.student_name) AS rank
FROM scores a
LEFT JOIN scores b
  ON b.score >= a.score
GROUP BY a.student_name, a.score
ORDER BY a.score DESC;

📈 结果:

张三 95 1 李四 92 2 王五 92 2 赵六 88 4 孙七 85 5
student_namescorerank

✅ 等价于 RANK() OVER (ORDER BY score DESC)

🧩 2️⃣ 模拟 DENSE_RANK()

思路:
统计“比当前行分数高的不同分数”数量,再加上自己。

SELECT 
    a.student_name,
    a.score,
    COUNT(DISTINCT b.score) AS dense_rank
FROM scores a
LEFT JOIN scores b
  ON b.score >= a.score
GROUP BY a.student_name, a.score
ORDER BY a.score DESC;

📈 结果:

张三 95 1 李四 92 2 王五 92 2 赵六 88 3 孙七 85 4
student_namescoredense_rank

✅ 等价于 DENSE_RANK() OVER (ORDER BY score DESC)

🧩 3️⃣ 模拟 ROW_NUMBER()

思路:
ROW_NUMBER() 必须每行唯一编号,不能并列。
所以在分数相同的情况下,还需要用另一个唯一字段打破并列(如 student_nameid)。

SELECT 
    a.student_name,
    a.score,
    COUNT(b.student_name) AS row_number
FROM scores a
LEFT JOIN scores b
  ON b.score > a.score
   OR (b.score = a.score AND b.student_name < a.student_name)
GROUP BY a.student_name, a.score
ORDER BY a.score DESC, a.student_name;

📈 结果:

张三 95 1 李四 92 2 王五 92 3 赵六 88 4 孙七 85 5
student_namescorerow_number

✅ 等价于 ROW_NUMBER() OVER (ORDER BY score DESC, student_name ASC)


PARTITION BY 详解

PARTITION BY用于在窗口函数中进行分组计算,类似于 GROUP BY的概念,但关键区别是:它不会合并行,而是在每个分组内独立进行计算

基本语法

函数名() OVER (
    PARTITION BY 分组字段 
    ORDER BY 排序字段
)

具体示例

假设我们有一个更详细的学生成绩表:

1 张三 一班 95 2 李四 一班 92 3 王五 一班 88 4 赵六 二班 96 5 孙七 二班 90 6 周八 二班 90 7 吴九 二班 85
student_idstudent_nameclass_namescore

查询语句:计算每个班级内部的排名

SELECT
    student_id,
    student_name,
    class_name,
    score,
    ROW_NUMBER() OVER (PARTITION BY class_name ORDER BY score DESC) AS rn,
    RANK() OVER (PARTITION BY class_name ORDER BY score DESC) AS rk,
    DENSE_RANK() OVER (PARTITION BY class_name ORDER BY score DESC) AS dr
FROM students;

查询结果:

1 张三 一班 95 1 1 1 2 李四 一班 92 2 2 2 3 王五 一班 88 3 3 3 4 赵六 二班 96 1 1 1 5 孙七 二班 90 2 2 2 6 周八 二班 90 3 2 2 7 吴九 二班 85 4 4 3
student_idstudent_nameclass_namescorernrkdr

结果分析

一班的情况:

  • 张三:95分,班级第1名(rn=1, rk=1, dr=1)
  • 李四:92分,班级第2名(rn=2, rk=2, dr=2)
  • 王五:88分,班级第3名(rn=3, rk=3, dr=3)

二班的情况(注意排名重置):

  • 赵六:96分,在二班内重新从第1名开始排名(rn=1, rk=1, dr=1)
  • 孙七周八:都是90分,出现并列
    • ROW_NUMBER():强制分配2和3(不确定顺序)
    • RANK():都排名第2,下一个吴九排名第4(跳过第3名)
    • DENSE_RANK():都排名第2,下一个吴九排名第3(连续)

PARTITION BY 的更多实用例子

1. 查询每个班级的前2名学生

SELECT * FROM (
    SELECT
        student_name,
        class_name,
        score,
        ROW_NUMBER() OVER (PARTITION BY class_name ORDER BY score DESC) as rank_in_class
    FROM students
) AS ranked_students
WHERE rank_in_class <= 2;

2. 计算每个学生的成绩在班级内的百分比排名

SELECT
    student_name,
    class_name,
    score,
    ROUND(
        PERCENT_RANK() OVER (PARTITION BY class_name ORDER BY score) * 100, 2
    ) as percentile_in_class
FROM students;

3. 与其他窗口函数结合使用

SELECT
    student_name,
    class_name,
    score,
    -- 班级内排名
    RANK() OVER (PARTITION BY class_name ORDER BY score DESC) as class_rank,
    -- 班级内平均分
    AVG(score) OVER (PARTITION BY class_name) as class_avg,
    -- 班级内最高分
    MAX(score) OVER (PARTITION BY class_name) as class_max
FROM students;

PARTITION BY 的核心优势

  1. 分组不合并:在每个分组内独立计算,但保持所有原始数据行
  2. 排名重置:每个分组的排名都从1重新开始
  3. 灵活组合:可以按多个字段分区:PARTITION BY class_name, subject_name
  4. 性能优化:比使用多个子查询或自连接更高效

PARTITION BY它实现了"既要分组计算,又要保留明细数据"的需求。