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

MySQL中的窗口函数

深入理解窗口函数(Window Functions)

窗口函数确实经常用于分组后为行分配序号(如1,2,3…),但它的功能远不止于此。窗口函数是SQL中极其强大的分析工具,可以让你在不减少行数的情况下进行复杂计算


窗口函数本质

窗口函数的核心特点是:

  • 不折叠行:与GROUP BY不同,窗口函数会保留所有原始行
  • 定义窗口:通过OVER()子句指定计算的数据范围(“窗口”)
  • 逐行计算:为每一行返回一个基于其所在窗口的计算结果

窗口函数家族

1. 排名类函数

  • ROW_NUMBER():连续唯一序号(1,2,3,4…)
  • RANK():相同值同排名,后续跳过(1,2,2,4…)
  • DENSE_RANK():相同值同排名,后续不跳过(1,2,2,3…)

2. 分析类函数

  • LEAD(列名, n):获取当前行后第n行的值
  • LAG(列名, n):获取当前行前第n行的值
  • FIRST_VALUE(列名):窗口第一个值
  • LAST_VALUE(列名):窗口最后一个值
  • NTH_VALUE(列名, n):窗口第n个值

3. 聚合函数作为窗口函数

  • SUM() OVER()
  • AVG() OVER()
  • COUNT() OVER()
  • MAX() OVER()
  • MIN() OVER()

窗口定义的三要素

窗口函数的强大之处在于可以精确控制"窗口"范围

函数() OVER([PARTITION BY 分组列][ORDER BY 排序列][ROWS/RANGE 框架]
)

1. PARTITION BY

将数据分成多个组,函数在每个组内独立计算(类似GROUP BY但不合并行

2. ORDER BY

定义分区内的排序方式,影响序号分配和滑动窗口计算

3. 窗口框架

窗口框架(Window Frame)​​ 是 SQL 窗口函数中一个 ​​高级但极其有用​​ 的功能,它允许你 ​​更精细地控制计算范围​​,而不仅仅是按 PARTITION BY 分组或按 ORDER BY 排序。

窗口框架的作用​​
在 OVER() 子句中,除了 PARTITION BY 和 ORDER BY,你还可以用 ​​ROWSRANGE​​ 来定义:

  1. ​​计算时包含哪些行​​(例如:当前行 + 前3行) ​​
  2. 是否包含当前行​​
  3. ​​是否包含未来的行(FOLLOWING)​​

​​典型应用场景​​
✅ ​​移动平均(Moving Average)​​
✅ ​​累计计算(Running Total)​​
✅ ​​前后行对比(Lag/Lead 分析)​​
✅ ​​滑动窗口统计(如最近5天的总和)​

基本语法

SUM(column) OVER([PARTITION BY ...][ORDER BY ...]ROWS|RANGE BETWEEN <start> AND <end>
)
  • ​​ROWS​​ → 按 ​​物理行​​ 计算(固定行数)
  • ​​RANGE​​ → 按 ​​逻辑范围​​ 计算(如相同值的行视为同一组)

窗口框架的边界选项

选项含义
UNBOUNDED PRECEDING从分区的第一行开始
n PRECEDING当前行之前的 n
CURRENT ROW当前行
n FOLLOWING当前行之后的 n
UNBOUNDED FOLLOWING直到分区的最后一行

3. 实际案例
案例1:计算3天移动平均(ROWS)

SELECT date,           -- 日期列revenue,        -- 当天的收入AVG(revenue) OVER(  -- 计算收入的移动平均值ORDER BY date   -- 按日期排序ROWS BETWEEN 2 PRECEDING AND CURRENT ROW  -- 包含当前行 + 前2行) AS moving_avg_3day  -- 结果列名
FROM sales;

关键点:ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
2 PRECEDING = 当前行的前2行
CURRENT ROW = 当前行
→ 合起来就是 当前行 + 前2行,共3行数据

假设数据是这样的:

daterevenue
2023-01-01100
2023-01-02150
2023-01-03200
2023-01-04250

查询结果会是:

daterevenuemoving_avg_3day计算方式
2023-01-01100100.0(100) / 1(只有1天数据)
2023-01-02150125.0(100 + 150) / 2
2023-01-03200150.0(100 + 150 + 200) / 3
2023-01-04250200.0(150 + 200 + 250) / 3

前几行不够怎么办?
• 比如第1天(2023-01-01),前面没有数据,就只算它自己。
• 第2天(2023-01-02),只有前1天的数据,就只算2天的平均。


案例2:计算累计到当前行的总和(RANGE)

# 计算销售数据的​​累计收入(running total)​​,也就是从最早日期到当前日期的收入总和
SELECT date,revenue,SUM(revenue) OVER( #对 revenue 列求和ORDER BY date #按日期排序RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW# 计算范围是从最早日期(UNBOUNDED PRECEDING)到当前行(CURRENT ROW)) AS running_total
FROM sales;

关键点解析

  1. RANGE vs ROWS
    RANGE:按逻辑范围计算(相同日期的行会被合并统计)。
    ROWS:按物理行计算(严格按行数计算,即使日期相同也会分开统计)。
  2. UNBOUNDED PRECEDING
    • 表示从分区的第一行开始计算(这里是按日期排序后的最早日期)。
  3. CURRENT ROW
    • 计算到当前行为止。

假设原始数据:

daterevenue
2023-01-01100
2023-01-02150
2023-01-03200

查询结果:

daterevenuerunning_total计算逻辑
2023-01-01100100100
2023-01-02150250100 (前一天) + 150
2023-01-03200450250 (前累计) + 200

案例3:计算当前行 + 前后各1行的总和(滑动窗口)

SELECT date,revenue,SUM(revenue) OVER(ORDER BY dateROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sliding_sum
FROM sales;

结果示例:

daterevenuesliding_sum
2023-01-01100250
2023-01-02150450
2023-01-03200600
2023-01-04250450

4. ROWS vs RANGE 的区别

类型行为适用场景
ROWS按 物理行 计算(固定行数)移动平均、滑动窗口
RANGE按 逻辑范围 计算(相同值的行视为同一组)处理重复值(如相同日期的数据)

示例(RANGE 处理相同日期的数据):

SELECT date,revenue,SUM(revenue) OVER(ORDER BY dateRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
FROM sales;

如果 2023-01-01 有2行数据(100 和 150),RANGE 会 同时计算这两行,而 ROWS 会逐行计算。


5. 总结
• 窗口框架(Window Frame) 让你可以 更灵活地定义计算范围,而不仅仅是按分区或排序计算。
ROWS → 适用于 固定行数 的计算(如移动平均)。
RANGE → 适用于 逻辑范围 的计算(如相同日期的数据)。
• 常见用途:移动平均、累计计算、滑动窗口统计。

实际应用示例

示例1:计算累计和

# 计算销售数据的每日收入以及累计收入
SELECT date,revenue,SUM(revenue) OVER(ORDER BY date) AS running_total
FROM sales;

示例2:计算同部门薪资排名

SELECT employee_name,department,salary,RANK() OVER(PARTITION BY department ORDER BY salary DESC) AS dept_rank
FROM employees;

示例3:计算3个月移动平均

SELECT month,sales,AVG(sales) OVER(ORDER BY month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg
FROM monthly_sales;

与GROUP BY的关键区别

特性窗口函数GROUP BY
行数保持原行数合并行
计算方式基于窗口计算基于分组计算
结果每行都有计算结果每组一行结果
使用场景排名、累计、移动平均等分析需求汇总统计、分组聚合

总结

窗口函数远不止是为行分配简单序号,它是:

  1. 一种强大的分析工具
  2. 可以访问与当前行相关的其他行
  3. 能够进行复杂计算而不减少行数
  4. 现代数据分析不可或缺的功能

掌握窗口函数可以让你解决许多传统SQL难以处理的分析问题,如:

  • 计算同比/环比增长率
  • 识别数据趋势
  • 处理复杂的排名和分组分析
  • 计算各种滑动窗口指标

补充

(1)RANGE 和 ROWS 在窗口函数中的区别?

核心区别
ROWS = 按物理行计算(数"行数")
RANGE = 按逻辑范围计算(看"值的大小")

具体区别

特性ROWSRANGE
计算方式按绝对行数按排序字段的值范围
相同值处理每行独立计算相同值会被合并计算
性能更快较慢(需要额外计算)
典型用途移动平均、固定行数计算处理重复值、按实际范围计算

实际例子说明
假设有这样的销售数据:

-- 测试数据
INSERT INTO sales VALUES('2023-01-01', 100),('2023-01-02', 150),('2023-01-02', 200), -- 注意这里有重复日期('2023-01-03', 50);

使用ROWS的查询

SELECT date,revenue,SUM(revenue) OVER(ORDER BY dateROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS rows_running_total
FROM sales;

结果:

date       | revenue | rows_running_total
-----------+---------+-------------------
2023-01-01 | 100     | 100    ← 第1行
2023-01-02 | 150     | 250    ← 100+150 (第1+2行)
2023-01-02 | 200     | 450    ← 250+200 (第1+2+3行) 
2023-01-03 | 50      | 500    ← 450+50

使用RANGE的查询

SELECT date,revenue,SUM(revenue) OVER(ORDER BY dateRANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS range_running_total
FROM sales;

结果:

date       | revenue | range_running_total
-----------+---------+--------------------
2023-01-01 | 100     | 100    ← 只有这一天
2023-01-02 | 150     | 450    ← 100+150+200 (所有1月2日的数据)
2023-01-02 | 200     | 450    ← 同上(相同日期被合并计算)
2023-01-03 | 50      | 500    ← 450+50

关键区别图示

数据行: [2023-01-01/100] → [2023-01-02/150] → [2023-01-02/200] → [2023-01-03/50]ROWS计算:  行1      行1+2     行1+2+3      行1+2+3+4[100]    [250]     [450]       [500]RANGE计算: date<=1/1  date<=1/2          date<=1/3[100]      [100+150+200]       [100+150+200+50][450]重复两次       [500]

什么时候用哪个?
**用 ROWS **
• 需要计算固定行数(如"最近3行")
• 数据没有重复排序值
• 需要更高性能

RANGE
• 排序字段可能有重复值(如相同日期)
• 需要按实际值范围计算(如"所有小于当前值的行")
• 做时间序列分析时更准确

进阶技巧
可以组合使用:

-- 计算当前日期及前2天(按日期范围)
SUM(revenue) OVER(ORDER BY dateRANGE BETWEEN INTERVAL '2' DAY PRECEDING AND CURRENT ROW
)
http://www.xdnf.cn/news/3639.html

相关文章:

  • Modbus 通讯协议(超详细,简单易懂)
  • Qt 中实现观察者模式(Observer Pattern)
  • Milvus(12):分析器
  • 虚拟机软件详解
  • AI日报 · 2025年5月03日|Perplexity 集成 WhatsApp,苹果传与 Anthropic 合作开发 Xcode
  • 青少年编程与数学 02-018 C++数据结构与算法 24课题、密码学算法
  • 【C#】一个类中的接口方法使用static和不使用static的区别
  • aidermacs开源程序使用 Aider 在 Emacs 中进行 AI 配对编程
  • 使用xlwings将excel表中将无规律的文本型数字批量转化成真正的数字
  • 自定义Dockerfile,发布springboot项目
  • Mysql进阶篇1_存储引擎、索引、SQL性能分析指令
  • 基于Jenkins的DevOps工程实践之Jenkins共享库
  • AVIOContext 再学习
  • Spring 容器相关的核心注解​
  • 19. LangChain安全与伦理:如何避免模型“幻觉“与数据泄露?
  • Linux电源管理(5)_Hibernate和Sleep功能介绍
  • ElasticSearch深入解析(九):Object、Nested、Flattened类型
  • 【RabbitMQ】 RabbitMQ快速上手
  • Python 函数装饰器和闭包(使用装饰器改进“策略”模式)
  • 玩转Docker | 使用Docker部署AI证件照工具
  • 【2025】ORM框架是什么?有哪些常用?Mybatis和Hibernate是什么样的?
  • ES6/ES11知识点
  • wpf CommandParameter 传递MouseWheelEventArgs参数 ,用 MvvmLight 实现
  • Word域操作记录(从1开始的毕业论文格式排版)
  • 神经网络的基本概念与深度解析——基于生物机制的仿生建模与工程实现
  • Spring Boot的GraalVM支持:构建低资源消耗微服务
  • Kubernetes(k8s)学习笔记(四)--入门基本操作
  • 双指针(5)——有效三角形个数
  • 杭电oj(1180、1181)题解
  • “淘宝闪购”提前4天全量,意味着什么?