浅谈 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.pswp.cn/news/920828.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/920828.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DSPFilters实现低通滤波器(QT)

DSPFilters实现低通滤波器DSPFilters实现低通滤波器DSPFilters安装-构建静态库QT代码复制include和静态库到qt项目qt代码配置效果DSPFilters实现低通滤波器 https://github.com/vinniefalco/DSPFilters DSPFilters安装-构建静态库 用 Qt 自带的 MinGW(最简单&…

mybatis plus 基本使用和源码解析

简介 mybatis-plus是一款mybatis增强工具,用于简化开发,提高效率。mybatis-plus免去了用户编写sql的麻烦,只需要创建好实体类,并创建一个继承自BaseMapper的接口,mybatis就可以自动生成关于单表的crud。mybatis-plus自…

【Android】Notification 的基本使用

文章目录【Android】Notification的基本使用权限通知的基本使用1. 获取通知管理器(用于发送、更新、取消通知)2. 创建通知渠道(Android 8.0 必须)3. 使用通知3.1 发送通知3.2 更新通知3.3 取消通知通知的进阶技巧通知显示样式1. B…

Web前端开发基础

1.前端概论 1.1 什么是前端? 概念:前端(Front-End),也称为客户端(Client-Side),指的是用户在使用网站或Web应用时直接看到并与之交互的部分。它涵盖了屏幕上的一切内容,从文字、图片、按钮、布局到动画效果 一个简单的…

并发编程——11 并发容器(Map、List、Set)实战及其原理分析

1 JUC包下的并发容器Java 基础集合(如 ArrayList、LinkedList、HashMap)非线程安全。为了解决线程安全问题,Java 最初提供了同步容器(如 Vector、Hashtable、SynchronizedList),但它们通过 synchronized 实…

Circuitjs 测试点的使用

在电路中, 有时候我们想知道, 各个节点上电压的具体的值. 比如下面这个电路:电流流经两个电阻器之后, 电压在下降. 如果想知道具体节点电压的确切数值, 可以通过添加 测试点(Test Point) 实现. 点击 绘制–输出和标签–添加测试点, 之后在所需测量的节点上拖动添加一个测试点, …

Ansible Playbook 实践

Ansible Playbook 实践一、Playbook 基础规范(一)YAML 格式要求文件标识:以 --- 开头,明确为 YAML 文件,结尾可加 ...(可选,用于标记文件结束)。注释规则:用 # 实现注释&…

基于 Vue + Interact.js 实现可拖拽缩放柜子设计器

实现可视化设计工具已成为前端开发的重要挑战之一。本文将介绍如何使用 Vue.js 配合 Interact.js 库创建一个功能完整的橱柜设计器,兼容PC和移动终端。核心功能网格系统:基于 CSS 网格实现精准定位拖拽功能:实现单元格的自由移动缩放控制&…

今日科技速递 | 智能芯片突围、AI+行动深化、服贸会科技成果亮相

今日科技速递 | 智能芯片突围、AI行动深化、服贸会科技成果亮相 一、乐鑫科技涨停:Wi-Fi 6/7 与 AIoT 芯片双路径创新驱动 新闻回顾 2025 年 8 月 27 日,科创板公司 乐鑫科技(688018) 盘中一度涨停,股价达到 225 元&am…

PDF压缩如何平衡质量与体积?

在日常工作或者生活中,我们常常要处理PDF文档,很多人可能会遇到这样的困扰?使用WPS处理PDF时,部分功能需要付费,这给我们带来了许多不便。 它的使用方式十分简单,你只要双击图标,它就能启动&am…

Flask 之上下文详解:从原理到实战

一、引言:为什么 Flask 需要“上下文”?在 Web 开发中,我们经常需要访问当前请求的信息(如 URL、表单数据)、当前应用实例(如配置、数据库连接)或用户会话状态。传统做法是使用全局变量&#xf…

深入探索Vue:前端开发的强大框架

在当今的前端开发领域,Vue作为一款备受瞩目的JavaScript框架,以其简洁易用、高效灵活等特性,赢得了众多开发者的青睐。无论是构建小型的交互页面,还是开发大型的单页应用,Vue都能展现出卓越的性能和出色的表现。本文将…

B树与B+树的原理区别应用

在磁盘存储和内存有序的数据管理中,B 树与 B 树是核心的数据结构,二者均通过 “多路平衡” 特性减少 IO 次数,但在数据存储方式、查询逻辑上存在本质差异。一、B 树(Balance Tree):多路平衡搜索树B 树是 “…

从零到一:使用anisble自动化搭建kubernetes集群

在我们云原生俱乐部的暑期学习中,我们了解并学习了需要关于云原生的技术,其中在应用层面上最重要的就是shell编程和ansible,而想要掌握这两项技术离不开的就是实践,而kubernetes是我们云原生技术栈的核心技术,在生产实…

【LangGraph】langgraph.prebuilt.create_react_agent() 函数:快速创建基于 ReAct(Reasoning + Acting)架构的智能代理

本文是对 langgraph.prebuilt.create_react_agent 函数的详细且全面的介绍,涵盖其定义、功能、设计理念、参数、返回值、使用场景、实现原理、示例代码、高级用法、注意事项、与其他方法的对比,以及学习建议。 1. 概述 langgraph.prebuilt.create_react…

北斗导航 | RAIM算法改进方案及性能对比分析报告

github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 文章目录RAIM算法改进方案及性能对比分析报告一、RAIM算法改进技术框架1.1 多假设分组算法(MHSS)1.2 动态噪声估计算法1.3 多源信息融合技术二、…

数据结构第8章 排序(竟成)

第 8 章 排序【考纲内容】1.排序的基本概念;2. 直接插入排序;3. 折半插入排序;4. 起泡排序(Bubble Sort);5.简单选择排序;6. 希尔排序(Shell Sort);7. 快速排…

【学Python自动化】 5. Python 数据结构学习笔记

一、 列表详解 1 列表方法总结方法描述等价操作rust Vec类似操作list.append(x)末尾添加元素a[len(a):] [x]vec.push(x);list.extend(iterable)扩展列表a[len(a):] iterablevec.extend([4, 5, 6]); 或者更高效:vec.extend_from_slice(&[4, 5, 6]);list.inser…

Python爬虫实战:研究Radar chart,构建多维度数据采集和分析系统

1. 引言 1.1 研究背景与意义 在信息爆炸的时代,互联网蕴含的海量数据已成为企业决策、学术研究和产品评估的重要依据。这些数据往往包含多个维度的特征,如电商平台的商品信息涵盖价格、销量、评价、性能参数等,社交媒体的用户数据涉及活跃度、互动量、内容偏好等。传统的单…

[灵动微电子 MM32BIN560CN MM32SPIN0280]读懂电机MCU之串口DMA

在 MM32SPIN560C 微控制器中,串口(UART)的 DMA 传输可大幅减轻 CPU 负担,实现数据的“自动收发”。结合《MM32SPIN560C 用户手册(中文版)》中 UART 和 DMA 相关章节,以下从“原理匹配”“配置步…