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

深度解析Mysql的开窗函数(易懂版)

SQL 开窗函数(Window Function)是一种强大的分析工具,它能在保留原有数据行的基础上,对 "窗口"(指定范围的行集合)进行聚合、排名或分析计算,解决了传统GROUP BY聚合会合并行的局限性。

一、开窗函数的核心特点

  • 不合并行:与GROUP BY不同,开窗函数计算后会保留所有原始行,只是为每行附加一个计算结果。
  • 窗口定义:通过OVER()子句定义 "窗口"(即计算范围),可按条件分区、排序或限定行范围。
  • 适用场景:排名(如 top N)、累计计算(如累计求和)、移动分析(如近 3 天平均值)、前后行数据获取等。

二、基本语法结构

开窗函数的通用语法:

函数名(参数) OVER ([PARTITION BY 分区列1, 分区列2...]  -- 可选:按列分组,每组独立计算[ORDER BY 排序列1 [ASC|DESC], ...]  -- 可选:分区内的排序方式[ROWS | RANGE 窗口范围]  -- 可选:定义窗口的具体行范围(行级窗口)
)

  • 函数名:可以是排名函数(RANK()ROW_NUMBER()等)、聚合函数(SUM()AVG()等)或分析函数(LAG()LEAD()等)。
  • OVER()子句:核心部分,用于定义 "窗口" 的规则。

三、OVER()子句详解

1. PARTITION BY:分区(分组)
  • 作用:将数据按指定列分成多个独立的 "分区",开窗函数在每个分区内单独计算(类似GROUP BY的分组,但不合并行)。
  • 示例:按 "部门" 分区,每个部门内部独立计算工资排名。
2. ORDER BY:分区内排序
  • 作用:指定分区内的行排序规则,影响排名函数的结果和窗口范围的界定。
  • 注意:若不指定PARTITION BY,则全表视为一个分区,按ORDER BY整体排序。
3. ROWS | RANGE:窗口范围(行级窗口)
  • 作用:在分区内,进一步限定参与计算的行范围(如 "当前行 + 前 2 行 + 后 1 行")。
  • 关键字:
    • ROWS:基于物理行数界定范围(如 "前 2 行")。
    • RANGE:基于值的逻辑范围界定(如 "值在当前行 ±10 以内的行"),仅支持数值 / 日期类型。
  • 常用范围表达式:
    • UNBOUNDED PRECEDING:分区的第一行
    • CURRENT ROW:当前行
    • n PRECEDING:当前行之前的第 n 行
    • n FOLLOWING:当前行之后的第 n 行
    • 组合示例:ROWS BETWEEN 2 PRECEDING AND 1 FOLLOWING(当前行 + 前 2 行 + 后 1 行)

四、常用开窗函数分类及示例

以下示例基于员工表employee,结构如下:

idnamedepartmentsalaryhire_date
1张三技术部80002020-01-15
2李四技术部90002019-03-20
3王五技术部90002018-05-10
4赵六市场部70002021-02-05
5钱七市场部85002020-08-18
1. 排名函数(用于生成排名)
(1)ROW_NUMBER():生成唯一序号
  • 功能:为分区内的每行分配一个连续的唯一序号(即使值相同,序号也不同)。
  • 示例:按部门分区,按工资降序排名(工资相同则按入职时间升序):
SELECT name, department, salary,ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC, hire_date ASC) AS row_num
FROM employee;
  • 结果:
    namedepartmentsalaryrow_num
    李四技术部90001(同工资,入职早排前)
    王五技术部90002
    张三技术部80003
    钱七市场部85001
    赵六市场部70002
(2)RANK():带跳号的排名
  • 功能:相同值排名相同,后续排名会 "跳号"(如两个第 1 名,下一个是第 3 名)。
  • 示例:按部门分区,按工资降序排名:
SELECT name, department, salary,RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rank_num
FROM employee;

  • 结果:
    namedepartmentsalaryrank_num
    李四技术部90001
    王五技术部90001(与李四并列第 1)
    张三技术部80003(跳号,直接第 3)
    钱七市场部85001
    赵六市场部70002
(3)DENSE_RANK():无跳号的排名
  • 功能:相同值排名相同,后续排名不跳号(如两个第 1 名,下一个是第 2 名)。
  • 示例:按部门分区,按工资降序排名:
SELECT name, department, salary,DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS dense_rank_num
FROM employee;

  • 结果:
    namedepartmentsalarydense_rank_num
    李四技术部90001
    王五技术部90001
    张三技术部80002(不跳号,第 2)
    钱七市场部85001
    赵六市场部70002
2. 聚合开窗函数(聚合函数 +OVER()

SUM()AVG()COUNT()等聚合函数与OVER()结合,为每行计算所在窗口的聚合结果。

(1)全分区聚合(无ORDER BY和范围)
  • 功能:计算整个分区的聚合值(每行的结果相同)。
  • 示例:计算每个部门的平均工资,附加到每行:
SELECT name, department, salary,AVG(salary) OVER (PARTITION BY department) AS dept_avg_salary
FROM employee;

  • 结果:
    namedepartmentsalarydept_avg_salary
    张三技术部80008666.67((8000+9000+9000)/3)
    李四技术部90008666.67
    王五技术部90008666.67
(2)累计聚合(带ORDER BY和范围)
  • 功能:按排序顺序计算 "累计" 聚合值(如累计求和、累计平均值)。
  • 示例:按部门分区,按入职时间升序,计算累计工资总和:
SELECT name, department, hire_date,salary,SUM(salary) OVER (PARTITION BY department ORDER BY hire_date ASCROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW  -- 从第一行到当前行) AS cumulative_salary
FROM employee;
  • 结果(技术部):
    namedepartmenthire_datesalarycumulative_salary
    王五技术部2018-05-1090009000(第一行,累计 = 自身)
    李四技术部2019-03-20900018000(累计 = 9000+9000)
    张三技术部2020-01-15800026000(累计 = 9000+9000+8000)
3. 分析函数(获取前后行数据)
(1)LAG(列名, n):获取当前行的前 n 行数据
  • 功能:返回当前行之前第 n 行的指定列值(默认 n=1)。
  • 示例:获取每个部门中,当前员工的前一位入职员工的工资:
SELECT name, department, hire_date,salary,LAG(salary, 1) OVER (PARTITION BY department ORDER BY hire_date ASC) AS prev_emp_salary
FROM employee;

  • 结果(技术部):
    namedepartmenthire_datesalaryprev_emp_salary
    王五技术部2018-05-109000NULL(第一行,无前一行)
    李四技术部2019-03-2090009000(前一行是王五的工资)
    张三技术部2020-01-1580009000(前一行是李四的工资)
(2)LEAD(列名, n):获取当前行的后 n 行数据
  • 功能:返回当前行之后第 n 行的指定列值(默认 n=1)。
  • 示例:获取每个部门中,当前员工的后一位入职员工的工资:
SELECT name, department, hire_date,salary,LEAD(salary, 1) OVER (PARTITION BY department ORDER BY hire_date ASC) AS next_emp_salary
FROM employee;

  • 结果(技术部):
    namedepartmenthire_datesalarynext_emp_salary
    王五技术部2018-05-1090009000(后一行是李四的工资)
    李四技术部2019-03-2090008000(后一行是张三的工资)
    张三技术部2020-01-158000NULL(最后一行,无后一行)

五、开窗函数与GROUP BY的区别

特性GROUP BY聚合开窗函数
行处理合并分组后的行(一行 / 组)保留所有原始行
计算范围整个分组可自定义窗口范围(分区、行范围)
结果列仅聚合结果 + 分组列原始列 + 开窗计算结果

六、注意事项

  1. 排序影响ORDER BY在开窗函数中不仅影响排名,还会影响窗口范围的界定(如累计计算)。
  2. 性能考量:复杂的窗口范围(如RANGE)可能导致性能下降,大表建议优先用ROWS
  3. 数据库支持:主流数据库(MySQL 8.0+、PostgreSQL、SQL Server、Oracle)均支持开窗函数,但部分细节可能有差异。

通过上述讲解,可掌握开窗函数的核心语法和应用场景。实际使用时,需根据业务需求灵活组合PARTITION BYORDER BY和窗口范围,实现复杂的数据分析。

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

相关文章:

  • docker-compose安装ElasticSearch,ik分词器插件,kibana【超详细】
  • 夜莺开源监控,模板函数一览
  • 集合,完整扩展
  • 任务调度系统设计与实现:Quartz、XXL-JOB 和 Apache Airflow 对比与实践
  • 【项目设计】高并发内存池
  • windows系统端口异常占用删除教程
  • Go面试题及详细答案120题(0-20)
  • [TryHackMe]Internal(hydra爆破+WordPress主题修改getshell+Chisel内网穿透)
  • 《Q————Mysql连接》
  • Linux软件编程:IO(二进制文件)、文件IO
  • 【25-cv-08993】T Miss Toys 启动章鱼宠物玩具版权维权,15 项动物玩偶版权均需警惕
  • 如何使用gpt进行模式微调(2)?
  • 使用Spring Boot对接欧州OCPP1.6充电桩:解决WebSocket连接自动断开问题
  • 无文件 WebShell攻击分析
  • php+apache+nginx 更换域名
  • SpringCloud 核心内容
  • 82. 删除排序链表中的重复元素 II
  • 计算机网络摘星题库800题笔记 第4章 网络层
  • “冒险玩家”姚琛「万里挑一」特别派对 打造全新沉浸式户外演出形式
  • Javase 之 字符串String类
  • 亚马逊手工制品类目重构:分类逻辑革新下的卖家应对策略与增长机遇
  • 高性能web服务器Tomcat
  • 嵌入式Linux内存管理面试题大全(含详细解析)
  • 元宇宙虚拟金融服务全景解析:技术创新、场景重构与未来趋势
  • 数据结构:链表栈的操作实现( Implementation os Stack using List)
  • LDAP 登录配置参数填写指南
  • 文件io ,缓冲区
  • 【智慧城市】2025年湖北大学暑期实训优秀作品(3):基于WebGIS的南京市古遗迹旅游管理系统
  • 简单的双向循环链表实现与使用指南
  • 小黑课堂计算机一级Office题库安装包2.93_Win中文_计算机二级考试_安装教程