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

SQL开窗函数

文章目录

  • 开窗函数
    • 简介
    • 为什么使用开窗函数
    • 使用开窗函数的经典场景
      • 场景1:排名
      • 场景2:累计计算
      • 场景3:移动平均/移动总计
      • 场景4:比较相邻行
      • 场景5:计算占比
      • 总结

开窗函数

简介

SQL的开窗函数是MYSQL在8版本后提供用来简化SQL语句的编写的一个方法
标志词是over()
它与我们的聚合函数类似
但是又不一致
聚合函数是在group by会分成几组,会压缩我们原本的数据信息


而我们的聚合函数可以直接在我们原本的数据行上添加一列我们想知道的信息

select*,avg(sal)over(partitionbyjob)fromemployee;

avg(sal) over(partition by job) 前一部分是运算的开窗函数,后一部分是进行数据处理的分区

这个语句是可以使用我们的 聚合函数+子查询进行复现的

SELECTe.*,e1.avg_salAS"avg(sal) over(partition by job)"FROMemployee eJOIN(SELECTjob,AVG(sal)ASavg_salFROMemployeeGROUPBYjob)e1ONe.job=e1.job;

那下面这条语句就很难简单的去实现了
累加值就是这个日期前的累加在一起
像这种以明细的方式进行累加是我们开窗函数的典型应用场景
!!!

sum(sal) over (partition by dname order by hiredate desc)
解释一下:就是按照dname进行分区嘛,然后分区里面根据hiredate 倒序排序
重要的是sum(sal)只会计算你当前和前面的累加数据,相当于是一个过程
而不是直接将所有工资一sum()就赋值给每个数据了

为什么使用开窗函数

使用开窗函数的经典场景

场景1:排名

需求:对员工按工资进行排名,展示不同排名函数的区别。
假设员工表employees只有四个字段:id(员工ID)、name(姓名)、salary(工资)、job(职业)。数据如下:

idnamesalaryjob
1Alice8000doctor
2Bob7000doctor
3Carol9000doctor
4David7000joker
5Eve8500joker
SELECTid,name,salary,job ROW_NUMBER()OVER(partitionbyjobORDERBYsalaryDESC)ASrow_number_rank,RANK()OVER(partitionbyjobORDERBYsalaryDESC)ASrank,DENSE_RANK()OVER(partitionbyjobORDERBYsalaryDESC)ASdense_rankFROMemployees;

结果

idnamesalaryjobrow_number_rankrankdense_rank
3Carol9000doctor111
1Alice8000doctor222
2Bob7000doctor333
5Eve8500joker111
4David7000joker222
  • 注意:分组生效:可以看到 Doctor 和 Joker 是分开排名的,各自的最高工资都是第 1 名。
  • ROW_NUMBER ():即使 Bob 和 David 工资相同(7000),它也会强制给出一个序号(3 和 4),就像排队一样,一人一个号,不并列。

排名函数区别

  • ROW_NUMBER():无论值是否相同,都按顺序分配唯一的连续整数(1,2,3,…)。
  • RANK():相同值的行排名相同,但会跳过并列占用的名次。例如:1,2,2,4(两个并列第二后,下一个是第四名)。
  • DENSE_RANK():相同值的行排名相同,且排名数字连续。例如:1,2,2,3(两个并列第二后,下一个是第三名)。

场景2:累计计算

需求:计算每个月的销售额累计值。
假设销售表sales只有两个字段:month(月份,格式为’YYYY-MM’)、amount(销售额)。数据如下:

monthamount
2023-011000
2023-021500
2023-031200
SELECTmonth,amount,SUM(amount)OVER(ORDERBYmonth)AScumulative_amountFROMsales;

结果

monthamountcumulative_amount说明
2023-0110001000
2023-0215002500(1000+1500)
2023-0312003700(1000+1500+1200)

说明:默认的窗口范围是从分区第一行到当前行(ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),所以是累计计算。

场景3:移动平均/移动总计

需求:计算3个月的移动平均销售额和移动总计。
使用与场景2相同的销售表。

SELECTmonth,amount,AVG(amount)OVER(ORDERBYmonthROWSBETWEEN2PRECEDINGANDCURRENTROW)ASmoving_avg,SUM(amount)OVER(ORDERBYmonthROWSBETWEEN2PRECEDINGANDCURRENTROW)ASmoving_sumFROMsales;

结果

monthamountmoving_avgmoving_sum说明
2023-0110001000.00001000– 只有当前行
2023-0215001250.00002500– 前1行+当前行(1000+1500)
2023-0312001233.33333700– 前2行+当前行(1000+1500+1200)
2023-0418001500.00004500– (1500+1200+1800) 注意:这里假设有4月数据

说明:通过ROWS BETWEEN 2 PRECEDING AND CURRENT ROW定义了窗口为当前行及其前两行(共3行)。移动平均和移动总计都是基于这个窗口计算。

场景4:比较相邻行

需求:计算本月销售额相比上月的增长额和增长率。
使用与场景2相同的销售表。

SELECTmonth,amount,LAG(amount)OVER(ORDERBYmonth)ASprev_amount,amount-LAG(amount)OVER(ORDERBYmonth)ASgrowth_amount,ROUND((amount-LAG(amount)OVER(ORDERBYmonth))/LAG(amount)OVER(ORDERBYmonth)*100,2)ASgrowth_rate_percentFROMsales;

结果

monthamountprev_amountgrowth_amountgrowth_rate_percent说明
2023-011000NULLNULLNULL
2023-021500100050050.00– (1500-1000)/1000*100 = 50%
2023-0312001500-300-20.00– (1200-1500)/1500*100 = -20%

说明

  • LAG(column, n):获取当前行向前数第n行的值(默认为1,即上一行)。
  • 第一行没有上一行,所以返回NULL。
  • 通过当前值减去上一行的值,可以得到增长额;再除以上一行的值,可以得到增长率。

场景5:计算占比

需求:计算每个员工工资占部门总工资的比例。
假设员工表employees有字段:id(员工ID)、name(姓名)、dept(部门)、salary(工资)。数据如下:

idnamedeptsalary
1AliceIT8000
2BobIT7000
3CarolHR9000
SELECTid,name,dept,salary,salary/SUM(salary)OVER(PARTITIONBYdept)*100ASdept_salary_percentFROMemployees;

结果

idnamedeptsalarydept_salary_percent说明
1AliceIT800053.33– 8000/(8000+7000)=53.33%
2BobIT700046.67– 7000/(8000+7000)=46.67%
3CarolHR9000100.00– 9000/9000=100%

说明:在窗口函数中,SUM(salary) OVER (PARTITION BY dept)计算每个部门的总工资。然后每个员工的工资除以部门总工资,得到该员工工资在部门中的占比。

总结

以上5个场景覆盖了开窗函数最常用的应用,每个例子都尽量精简字段,并详细解释了关键细节。开窗函数的核心在于通过OVER子句定义窗口(分区和排序),结合不同的窗口函数(如ROW_NUMBER,RANK,DENSE_RANK,SUM,AVG,LAG等)实现复杂的分析计算。

http://www.jsqmd.com/news/727277/

相关文章:

  • 零失败交付指南:Frappe测试框架的单元与集成测试全流程
  • 3分钟掌握Adobe Illustrator批量替换技巧:ReplaceItems脚本完全指南
  • Docker GitHub Actions Runner 高级配置:企业级安全与多架构支持实践
  • Oracle 创建视图报错:列名不唯一
  • 情绪化AI调教师认证:引领测试从业者的职业新赛道
  • Marmot监控与运维:Prometheus指标收集与告警设置全指南
  • LRC乐山无线电原装一级代理分销经销
  • 河北冲孔网厂家排行:五家实体厂商实力对比 - 奔跑123
  • ROFL播放器终极指南:一键解决英雄联盟回放版本限制问题
  • Nature性能优化技巧:10个提升应用性能的实用方法
  • 数控编程Mastercam 2026百度云盘下载与安装教程指南
  • SQL示例:为什么薪资表需要关联多次
  • 4月30日成都地区正大产镀锌钢管(Q235B;内径DN15-200mm)批发价格 - 四川盛世钢联营销中心
  • FastUI终极指南:无需JavaScript的React应用开发新范式
  • Oxy Forward中间件详解:如何实现高效的HTTP请求转发和头部重写
  • 2026年知网AI检测杀疯了?论文党亲测6招救命攻略必收藏! - 降AI实验室
  • SpringBoot+Vue微信小程序图片上传与展示全流程(含本地服务器配置)
  • 第3章 C程序的基本结构【20260430-001篇】
  • 地缘技术合规官
  • 如何永久保存微信聊天记录:WeChatMsg完整指南与深度分析
  • 自动化构建技能设计:从Webhook到CI/CD的轻量级实现
  • awesome-cdk安全实践:5个关键步骤保护你的云基础设施
  • Tesseract 开源OCR引擎深度解析:架构剖析与集成指南
  • 阿贝云
  • 塞尔达传说:旷野之息存档编辑器GUI - 新手玩家的终极修改指南
  • Linkerd2-proxy负载均衡机制:基于延迟的智能流量分发实战
  • 【AI】本地模型部署
  • [特殊字符]收藏不踩坑!100个Windows AD域渗透实战全流程+蓝队防护指南 附靶机资源
  • Pingu在WSL环境中的完整部署教程
  • awesome-cdk无密码认证:使用Cognito构建安全的登录系统