SQL开窗函数

文章目录

开窗函数

简介

SQL的开窗函数是MYSQL在8版本后提供用来简化SQL语句的编写的一个方法

标志词是over()

它与我们的聚合函数类似

但是又不一致

聚合函数是在group by会分成几组,会压缩我们原本的数据信息

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

sql 复制代码
select *,avg(sal) over(partition by job) from employee;

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

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

sql 复制代码
SELECT e.*, e1.avg_sal AS "avg(sal) over(partition by job)"
FROM employee e
JOIN (
    SELECT job, AVG(sal) AS avg_sal 
    FROM employee 
    GROUP BY job
) e1 ON e.job = e1.job;

那下面这条语句就很难简单的去实现了

累加值就是这个日期前的累加在一起

像这种以明细的方式进行累加是我们开窗函数的典型应用场景

!!!

sum(sal) over (partition by dname order by hiredate desc)

解释一下:就是按照dname进行分区嘛,然后分区里面根据hiredate 倒序排序

重要的是sum(sal)只会计算你当前和前面的累加数据,相当于是一个过程

而不是直接将所有工资一sum()就赋值给每个数据了

为什么使用开窗函数

使用开窗函数的经典场景

场景1:排名

需求 :对员工按工资进行排名,展示不同排名函数的区别。

假设员工表employees只有三个字段:id(员工ID)、name(姓名)、salary(工资)。数据如下:

id name salary
1 Alice 8000
2 Bob 7000
3 Carol 9000
4 David 7000
5 Eve 8500
sql 复制代码
SELECT 
    id, name, salary,
    ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_number_rank,
    RANK() OVER (ORDER BY salary DESC) AS rank,
    DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank
FROM employees;

结果

id name salary row_number_rank rank dense_rank
3 Carol 9000 1 1 1
5 Eve 8500 2 2 2
1 Alice 8000 3 3 3
2 Bob 7000 4 4 4
4 David 7000 5 4 4
  • 注意:这里Bob和David工资相同,但row_number强制给了不同序号
  • 因为Bob和David并列第4名(rank=4),所以下一个rank跳到了6(如果有下一个),但dense_rank接着是5

排名函数区别

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

场景2:累计计算

需求 :计算每个月的销售额累计值。

假设销售表sales只有两个字段:month(月份,格式为'YYYY-MM')、amount(销售额)。数据如下:

month amount
2023-01 1000
2023-02 1500
2023-03 1200
sql 复制代码
SELECT 
    month, 
    amount,
    SUM(amount) OVER (ORDER BY month) AS cumulative_amount
FROM sales;

结果

month amount cumulative_amount 说明
2023-01 1000 1000
2023-02 1500 2500 (1000+1500)
2023-03 1200 3700 (1000+1500+1200)

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

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

需求 :计算3个月的移动平均销售额和移动总计。

使用与场景2相同的销售表。

sql 复制代码
SELECT 
    month, 
    amount,
    AVG(amount) OVER (
        ORDER BY month
        ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
    ) AS moving_avg,
    SUM(amount) OVER (
        ORDER BY month
        ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
    ) AS moving_sum
FROM sales;

结果

month amount moving_avg moving_sum 说明
2023-01 1000 1000.0000 1000 -- 只有当前行
2023-02 1500 1250.0000 2500 -- 前1行+当前行(1000+1500)
2023-03 1200 1233.3333 3700 -- 前2行+当前行(1000+1500+1200)
2023-04 1800 1500.0000 4500 -- (1500+1200+1800) 注意:这里假设有4月数据

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

场景4:比较相邻行

需求 :计算本月销售额相比上月的增长额和增长率。

使用与场景2相同的销售表。

sql 复制代码
SELECT 
    month, 
    amount,
    LAG(amount) OVER (ORDER BY month) AS prev_amount,
    amount - LAG(amount) OVER (ORDER BY month) AS growth_amount,
    ROUND((amount - LAG(amount) OVER (ORDER BY month)) / LAG(amount) OVER (ORDER BY month) * 100, 2) AS growth_rate_percent
FROM sales;

结果

month amount prev_amount growth_amount growth_rate_percent 说明
2023-01 1000 NULL NULL NULL
2023-02 1500 1000 500 50.00 -- (1500-1000)/1000*100 = 50%
2023-03 1200 1500 -300 -20.00 -- (1200-1500)/1500*100 = -20%

说明

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

场景5:计算占比

需求 :计算每个员工工资占部门总工资的比例。

假设员工表employees有字段:id(员工ID)、name(姓名)、dept(部门)、salary(工资)。数据如下:

id name dept salary
1 Alice IT 8000
2 Bob IT 7000
3 Carol HR 9000
sql 复制代码
SELECT 
    id, name, dept, salary,
    salary / SUM(salary) OVER (PARTITION BY dept) * 100 AS dept_salary_percent
FROM employees;

结果

id name dept salary dept_salary_percent 说明
1 Alice IT 8000 53.33 -- 8000/(8000+7000)=53.33%
2 Bob IT 7000 46.67 -- 7000/(8000+7000)=46.67%
3 Carol HR 9000 100.00 -- 9000/9000=100%

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

总结

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

相关推荐
Android采码蜂12 分钟前
BLASTBufferQueue03-BufferQueueConsumer核心操作
android
Android采码蜂21 分钟前
BLASTBufferQueue02-BufferQueueProducer核心操作
android
2501_915921431 小时前
没有Mac如何完成iOS 上架:iOS App 上架App Store流程
android·ios·小程序·https·uni-app·iphone·webview
码农明明2 小时前
Google Play 应用上架二三事
android·google
cpsvps_net3 小时前
表达式索引海外云持久化实践:关键技术解析与性能优化
性能优化
红橙Darren4 小时前
手写操作系统 - 环境搭建
android·微信·操作系统
_一条咸鱼_4 小时前
Android Runtime直接内存管理原理深度剖析(73)
android·面试·android jetpack
你听得到114 小时前
揭秘Flutter图片编辑器核心技术:从状态驱动架构到高保真图像处理
android·前端·flutter
wilinz4 小时前
Flutter Android 端接入百度地图踩坑记录
android·flutter
拾光拾趣录5 小时前
前端静态资源本地缓存:从秒开到省流量
前端·性能优化·浏览器