当前位置: 首页 > news >正文

浅谈 SQL 窗口函数:ROW_NUMBER() 与聚合函数的妙用

在日常开发中,我们经常会遇到这样的需求:既要保留明细数据,又要对数据进行排名、累计、分区统计。如果仅依赖传统的 GROUP BY,往往需要做多次子查询或者复杂的 JOIN,既繁琐又低效。

窗口函数(Window Function) 就是为了解决这类问题而生的。它的最大特点是:保留所有行,并在每一行的基础上增加一个“运算结果列”。


窗口函数的执行过程

可以把窗口函数的执行理解为以下三个步骤:

  1. 得到基础结果集
    先执行 FROMWHEREGROUP BYHAVING 等,形成基础结果集。此时还没有窗口函数的列。

  2. 在结果集上运算
    根据 OVER (PARTITION BY ... ORDER BY ...) 指定的分区规则和排序规则,对结果集的一组行进行计算。

  3. 合并结果
    把窗口函数的计算值作为新列,附加到结果集的每一行。最终输出就是“原始列 + 窗口函数列”。

⚡ 和 GROUP BY 的最大区别在于:

  • GROUP BY压缩行,一组只保留一行。
  • 窗口函数会保留所有行,只是多了一列运算结果。

实验准备

首先,我们创建两张用于实验的数据库表。

CREATE TABLE students (id     INT PRIMARY KEY,name   VARCHAR(50) NOT NULL,score  INT NOT NULL
);-- 插入示例数据
INSERT INTO students (id, name, score) VALUES
(1, '张三', 95),
(2, '李四', 88),
(3, '王五', 88),
(4, '赵六', 72);CREATE TABLE sales (id       INT PRIMARY KEY,region   VARCHAR(20) NOT NULL,salesman VARCHAR(50) NOT NULL,amount   INT NOT NULL
);-- 插入示例数据
INSERT INTO sales (id, region, salesman, amount) VALUES
(1, '东区', '张三', 1000),
(2, '东区', '李四', 1200),
(3, '西区', '王五', 800),
(4, '西区', '赵六', 950),
(5, '西区', '孙七', 700);

例子一:ROW_NUMBER() 全局排序

students 表信息如下:

idnamescore
1张三95
2李四88
3王五88
4赵六72

我们想给所有学生按成绩排名:

SELECT id, name, score,ROW_NUMBER() OVER (ORDER BY score DESC, id ASC) AS rn
FROM students;

结果:

idnamescorern
1张三951
2李四882
3王五883
4赵六724

其中

  • ROW_NUMBER() → 为每行分配一个顺序号;
  • ORDER BY score DESC, id ASC → 排序规则,分数高的排前面;
  • 最终每一行都多了一个 rn 列,表示它的名次。

例子二:ROW_NUMBER() 分区内排序

再来看sales 表中的信息:

idregionsalesmanamount
1东区张三1000
2东区李四1200
3西区王五800
4西区赵六950
5西区孙七700

需求:在每个区域内,按销售额从高到低排名。

SELECTid,region,salesman,amount,ROW_NUMBER() OVER (PARTITION BY regionORDER BY amount DESC) AS rn
FROM sales;

结果:

idregionsalesmanamountrn
2东区李四12001
1东区张三10002
4西区赵六9501
3西区王五8002
5西区孙七7003

其中

  • PARTITION BY region → 按区域分区,每个区域单独排名。
  • ROW_NUMBER() → 每个区域内部从 1 开始编号。
  • 最终结果集仍然包含所有行,只是多了一个“区域内排名”的列。

例子三:SUM() OVER 分区聚合

继续使用 sales 表。
需求:在保留明细行的同时,显示该销售人员所在区域的总销售额。

SELECTid,region,salesman,amount,SUM(amount) OVER (PARTITION BY region) AS region_total
FROM sales;

结果:

idregionsalesmanamountregion_total
1东区张三10002200
2东区李四12002200
3西区王五8002450
4西区赵六9502450
5西区孙七7002450

其中

  • SUM(amount) OVER (PARTITION BY region) → 在每个区域内计算总额;
  • 结果仍然返回 5 行,只是每行多了一个“区域总额”的列。

窗口函数 vs GROUP BY

特性窗口函数 (Window Functions)GROUP BY 聚合 (Aggregate)
是否保留明细行✅ 保留所有行,只是在每行后面加一个新列❌ 会压缩行,每个分组只保留一行
典型用途排名 (ROW_NUMBER)、分区统计 (SUM OVER)、累计值、移动平均等分组统计 (SUM、AVG、COUNT、MAX、MIN 等)
是否依赖 PARTITION BY可选。PARTITION BY 决定分区范围,不写则针对全表计算必须分组,语义上天然就是“按分组聚合”
ORDER BY 的作用在窗口内排序,影响计算结果(如行号、累计和)在最终结果集中排序,对聚合计算无影响
返回结果行数与原始结果集相同行数 = 分组数(通常远少于原始行数)
复杂度一般更直观,避免子查询和 JOIN,常用于分析型 SQL用于汇总报表,逻辑简单,但不能同时保留明细数据
举例SUM(amount) OVER (PARTITION BY region)SELECT region, SUM(amount) FROM sales GROUP BY region;

对比一下 GROUP BY

SELECT region, SUM(amount)
FROM sales
GROUP BY region;

结果只有 2 行:

regionsum
东区2200
西区2450

可以看到:

  • GROUP BY 会把多行压缩成一行。
  • 窗口函数则保留明细,把聚合结果“合并回去”。

所以:

  • GROUP BY:适合做汇总统计,结果行数减少。
  • 窗口函数:适合做分析计算,保留明细又能展示分组/累计/排名结果。

总结

  1. 窗口函数执行顺序:先生成基础结果集,再对结果集进行分区/排序运算,最后把结果加到每行。

  2. 保留所有行:窗口函数不会减少行数,只会增加新列。

  3. 典型应用场景

    • 排名:ROW_NUMBER()RANK()DENSE_RANK()
    • 分区统计:SUM() OVER (PARTITION BY ...)
    • 累计计算:SUM() OVER (ORDER BY ...)
    • 移动平均、窗口滑动分析

掌握窗口函数,可以极大简化 SQL 写法,让我们的代码更直观、更高效。

http://www.dtcms.com/a/359762.html

相关文章:

  • 深入解析数据结构之单链表
  • 人工智能加速漏洞利用,15分钟即可完成概念验证?
  • 网络:相比于HTTP,HTTPS协议到底安全在哪?
  • go 开发环境配置 air + dlv debug 踩坑之旅
  • AI基础学习周报十一
  • 大模型——利用RAG构建智能问答平台实战
  • 图像描述编辑器 (Image Caption Editor)
  • 文字的力量:Qwen-Image如何让AI真正“读懂”中文之美
  • HTTPS -> HTTP 引起的 307 状态码与HSTS
  • ans.1中的对象标识符OBJECT_IDENTIFIER----OID
  • 【开题答辩全过程】以 基于springboot的垃圾分类管理系统为例,包含答辩的问题和答案
  • 力扣热题100:合并区间详解(Java实现)(56)
  • 历史数据分析——寒武纪
  • Android开发-活动页面
  • 20.28 《4bit量化模型预处理揭秘:如何节省75%显存高效微调LLM?》
  • leetcode-hot-100(堆)
  • 金融学-货币理论
  • Kafka应用过程中的高频问题
  • 【Linux基础】深入理解计算机存储:GPT分区表详解
  • 对于牛客网—语言学习篇—编程初学者入门训练—复合类型:BC136 KiKi判断上三角矩阵及BC139 矩阵交换题目的解析
  • uvm验证环境中struct(结构体)和class的区别与联系
  • 使用AdaLoRA 自适应权重矩阵微调大模型介绍篇
  • 接口测试总结-含接口测试和前端测试的区别与比较
  • PyTorch 张量(Tensor)详解:从基础到实战
  • 1.9 初始Memory Profiler Package
  • 面试 八股文 经典题目 - HTTPS部分(一)
  • Qt组件布局的经验
  • 深度学习数据加载实战:从 PyTorch Dataset 到食品图像分类全流程解析
  • 实现需求精准预测、运输路径优化及库存高效管理的智慧物流开源了
  • 利用 Java 爬虫获取淘宝拍立淘 API 接口数据的实战指南