Mysql窗口函数

1 什么是窗口函数

MySQL从8.0开始支持窗口函数,有的也叫分析函数(处理相对复杂的报表统计分析场景),这个功能在大多商业数据库和部分开源数据库中早已支持。

窗口函数:窗口、函数(应用在窗口内的函数)-----窗口类似于窗户,限定一个空间范围

那什么叫窗口呢?

窗口的概念非常重要,它可以理解为记录集合,窗口函数也就是在满足某种条件的记录集合上执行的特殊函数。对于每条记录都要在此窗口内执行函数,窗口大小都是固定的,这种属于静态窗口;不同的记录对应着不同的窗口,这种动态变化的窗口叫滑动窗口。

窗口函数的基本用法如下:

函数名([expr]) over 子句
函数() over()

其中,over是关键字,用来指定函数执行的窗口范围,包含三个分析子句:分组(partition by)子句,排序(order by)子句,窗口(rows)子句,如果后面括号中什么都不写,则意味着窗口包含满足where条件的所有行,窗口函数基于所有行进行计算;如果不为空,则支持以下语法来设置窗口:

函数名([expr]) over(partition by <要分列的组> order by <要排序的列> rows
between <数据范围>)
  • 知识点总结

sum(...A...) over(partition by ...B... order by ...C... rows between ...D1... and ...D2...)

avg(...A...) over(partition by ...B... order by ...C... rows between ...D1... and ...D2...)

A: 需要被加工的字段名称

B: 分组的字段名称

C: 排序的字段名称

D: 计算的行数范围

rows between 2 preceding and current row # 取当前行和前面两行rows between unbounded preceding and current row # 包括本行和之前所有的行rows between current row and unbounded following # 包括本行和之后所有的行rows between 3 preceding and current row # 包括本行和前面三行rows between 3 preceding and 1 following # 从前面三行和下面一行,总共五行# 当order by后面缺少窗口从句条件,窗口规范默认是rows between unbounded preceding and current row.# 当order by和窗口从句都缺失, 窗口规范默认是 rows between unbounded preceding and unbounded following

2 窗口函数应用

一般,我们可以把窗口函数分为两种:

专有窗口函数:

  • rank()
  • dense_rank()
  • row_number()

聚合类窗口函数:

普通场景下,聚合函数往往和group by一起使用,但是窗口环境下,聚合函数也可以应 用进来,那么此时它们就被称之为聚合类窗口函数,属于窗口函数的一种

  • sum()
  • count()
  • avg()
  • max()
  • min()

窗口函数(专有窗口函数+聚合类窗口函数)和普通场景下的聚合函数也很容易混淆,二者区别如下:

  • 普通场景下的聚合函数是将多条记录聚合为一条(多到一);窗口函数是每条记录都会执行,有几条记录执行完还是几条(多到多)。
  • 分组(partition by):记录按照字段进行分组,窗口函数在不同的分组上分别执行。
  • 排序(order by):按照哪些字段进行排序,窗口函数将按照排序后的记录顺序进行编号,可以和partition子句配合使用,也可以单独使用。如果没有partition子句,数据范围则是整 个表的数据行。
  • 窗口(rows):就是进行函数分析时要处理的数据范围,属于当前分区的一个子集,通常用来作为滑动窗口使用。比如要根据每个订单动态计算包括本订单和按时间顺序前后两个订单的移动平均支付金额,则可以设置rows子句来创建滑动窗口(rows)。

现有2018~2020某电商平台订单信息表user_trade

表结构如下:

列名释义
user_name用户名
piece购买数量
price价格
pay_amount支付金额
goods_category商品品类
pay_time支付日期
# 建立数据表
use legou;
create table user_trade
(user_name varchar(20),piece int,price double,pay_amount double,goods_category varchar(20),pay_time date
);

查看前10行数据

------累计计算函数应用、排序函数应用、偏移分析函数应用

2.1 累计计算函数

  • 累计求和:sum() over()
需求1: 查询出2019年每月的支付总额和当年累积支付总额
/*
需求1: 查询出2019年每月的支付总额和当年累积支付总额
*/-- step1 过滤出2019年数据
select * from user_trade where year(pay_time)=2019;-- step2 在1的基础上,按照月份进行group by 分组,统计每个月份的支付总额
SELECT MONTH( pay_time ),sum( pay_amount )
FROMuser_trade
WHEREYEAR ( pay_time ) = 2019
GROUP BYMONTH ( pay_time );-- step3 在2的基础上应用窗口函数实现需求
SELECTa.MONTH,-- 月份a.pay_amount,-- 当月总支付金额sum( a.pay_amount ) over ( ORDER BY a.MONTH ) -- 就是2019年的数据,所以
不用分组
-- --此时没有使用rows指定窗口数据范围,默认当前行及其之前的所有行FROM(SELECT MONTH( pay_time ) MONTH,sum( pay_amount ) pay_amountFROMuser_tradeWHEREYEAR ( pay_time ) = 2019GROUP BYMONTH ( pay_time )) a

结果如下:

需求2:查询出2018-2019年每月的支付总额和当年累积支付总额
SELECT a.year,
a.month,
a.pay_amount,
sum(a.pay_amount) over(partition by a.year order by a.month)
FROM
(SELECT year(pay_time) year,
month(pay_time) month,
sum(pay_amount) pay_amount
FROM user_trade
WHERE year(pay_time) in (2018,2019)
GROUP BY year(pay_time),
month(pay_time))a;

结果如下:

  • 移动平均:avg() over()
需求3: 查询出2019年每个月的近三月移动平均支付金额
SELECT a.month,a.pay_amount,avg(a.pay_amount) over(order by a.month rows between 2 preceding
and current row)
FROM(SELECT month(pay_time) month,sum(pay_amount) pay_amountFROM user_tradeWHERE year(pay_time)=2019GROUP BY month(pay_time))a;

结果如下:

  • 最大/小值:max()/min() over()
需求4: 查询出每四个月的最大月总支付金额
SELECT a.month,a.pay_amount,max(a.pay_amount) over(order by a.month rows between 3 preceding
and current row)
FROM(SELECT substr(pay_time,1,7) as month,sum(pay_amount) as pay_amountFROM user_tradeGROUP BY substr(pay_time,1,7))a;

结果如下;

2.2 排序函数

  • row_number() over(......)
  • rank() over(......)
  • dense_rank() over(......)
需求5: 2020年1月,购买商品品类数的用户排名
/*
需求5: 2020年1月,购买商品品类数的用户排名
2020年1月(基础数据范围)
一个商品属于某一个品类
A用户购买了100件商品,那么可能涉及到了10个品类
B用户购买了50件商品,那么可能涉及到了15个品类
根据所购买商品涉及的品类数量进行排名(给用户)
思路:
1)先把各个用户所购买商品涉及的品类数给统计出来
2) 在1的基础上排名,使count()用到排名窗口函数
*/-- 1)先把各个用户所购买商品涉及的品类数给统计出来
SELECTuser_name,count( DISTINCT goods_category ) category_count,row_number() over(order by count( DISTINCT goods_category ) ) order1,
-- row_number生成了行的编号从1开始rank() over(order by count( DISTINCT goods_category ) ) order2,dense_rank() over(order by count( DISTINCT goods_category ) ) order3
FROMuser_trade
WHEREsubstring( pay_time, 1, 7 ) = '2020-01'
GROUP BYuser_name;

结果如下:

  • 知识点总结

这三个函数的作用都是返回相应规则的排序序号

row_number() over(partition by ...A... order by ...B... )

rank() over(partition by ...A... order by ...B... )

dense_rank() over(partition by ...A... order by ...B... )

A:分组的字段名称

B:排序的字段名称

注意:

  • row_number()、rank() 和dense_rank()紧邻的括号内是不加任何字段名称的。
  • row_number:它会为查询出来的每一行记录生成一个序号,依次排序且不会重复。
  • rank&dense_rank:如果使用rank函数来生成序号,over子句中排序字段值相同的序号是一样 的,后面字段值不相同的序号将跳过相同的排名号排下一个,也就是相关行之前的排名数加一。
  • dense_rank函数在生成序号时是连续的,而rank函数生成的序号有可能不连续。
  • dense_rank函数出现相同排名时,将不跳过相同排名号,rank值紧接上一次的rank值。
  • 在各个分组内,rank()是跳跃排序,有两个第一名时接下来就是第三名,dense_rank()是连续排 序,有两个第一名时仍然跟着第二名。

  • ntile(n) over(......)
需求6: 查询出将2020年2月的支付用户,按照支付金额分成5组后的结果
SELECT user_name,sum(pay_amount) pay_amount,ntile(5) over(order by sum(pay_amount) desc) level
FROM user_trade
WHERE substr(pay_time,1,7)='2020-02'
GROUP BY user_name;

结果如下:

需求7: 查询出2020年支付金额排名前30%的所有用户
SELECT a.user_name,a.pay_amount,a.level
FROM(SELECT user_name,sum(pay_amount) pay_amount,ntile(10) over(order by sum(pay_amount) desc) levelFROM user_tradeWHERE year(pay_time)=2020GROUP BY user_name)a
WHERE a.level in (1,2,3);

结果如下:

  • 知识点总结

ntile(n) over(partition by ...A... order by ...B... )

n:切分的片数

A:分组的字段名称

B:排序的字段名称

ntile(n),用于将分组数据按照顺序切分成n片,返回当前切片值 NTILE不支持ROWS BETWEEN。

2.3 偏移分析函数

  • lag(...) over(......)
  • lead(...) over(......)
需求8: 查询出King和West的时间偏移(前N行)
SELECT user_name,pay_time,lag(pay_time,1,pay_time) over(partition by user_name order by pay_time) lag1,-- 没有传入偏移量,那么默认就是1,找不到的话,此处也没有给默认值,为nulllag(pay_time) over(partition by user_name order by pay_time) lag1_s,lag(pay_time,2,pay_time) over(partition by user_name order by pay_time) lag2,lag(pay_time,2) over(partition by user_name order by pay_time) lag2_s
FROM user_trade
WHERE user_name in ('King','West');

结果如下:

需求9: King和West的时间偏移(后N行)
SELECT user_name,pay_time,lead(pay_time,1,pay_time) over(partition by user_name order by pay_time) lead1,lead(pay_time) over(partition by user_name order by pay_time) lead2,lead(pay_time,2,pay_time) over(partition by user_name order by pay_time) lead3,lead(pay_time,2) over(partition by user_name order by pay_time) lead4
FROM user_trade
WHERE user_name in ('King','West');

结果如下:

  • 知识点总结

Lag和Lead函数可以在同一次查询中取出同一字段的前N行的数据(Lag)和后N行的数据(Lead) 作为独立的列。

在实际应用当中,若要用到取今天和昨天的某字段差值时,Lag和Lead函数的应用就显得尤为重要。 lag(exp_str,offset,defval) over(partion by ......order by ......)

lead(exp_str,offset,defval) over(partion by ......order by ......)

exp_str是字段名称。 offset是偏移量,即是上1个或上N个的值,假设当前行在表中排在第5行,则offset 为3,则表示我们所要找的数据行就是表中的第2行(即5-3=2)。offset默认值为1。defval默认值,当两个函数取上N/下N个值,当在表中从当前行位置向前数N行已经超出了表的范围时,lag()函数将defval这个参数值作为函数的返回值,若没有指定默认值,则返回NULL,那么在数学运算中,总要给一个默认值才不会出错。

  • 补充练习:
 需求10: 查询出支付时间间隔超过100天的用户数
SELECT count(distinct user_name)
FROM(SELECT user_name,pay_time,lead(pay_time) over(partition by user_name order by pay_time) lead_dtFROM user_trade)a
WHERE a.lead_dt is not null
and datediff(a.lead_dt,a.pay_time)>100;

结果如下:

需求11: 查询出每年支付时间间隔最长的用户
SELECTyears,b.user_name,b.pay_days
FROM(SELECTyears,a.user_name,datediff(a.pay_time,a.lag_dt) pay_days,rank() over(partition by years order by datediff(a.pay_time,a.lag_dt) desc) rank1FROM(SELECTyear(pay_time) as years,user_name,pay_time,lag(pay_time) over(partition by user_name,year(pay_time) order by pay_time) lag_dtFROM user_trade)aWHERE a.lag_dt is not null)b
WHERE b.rank1=1;

结果如下:

发布于 2023-07-19 09:21・IP 属地上海

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/194293.shtml

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

相关文章

P-Tuning v2论文概述

P-Tuning v2论文概述 P-Tuning v2论文概述前言微调的限制性P-Tuning的缺陷P-Tuning v2 摘要论文十问NLU任务优化点实验数据集预训练模型实验结果消融实验 结论 P-Tuning v2论文概述 前言 微调的限制性 微调&#xff08;fine-tuning&#xff09;是一种在预训练模型基础上进行目…

中国人工智能

随着科技的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;作为一项前沿技术在各个领域展现出了强大的潜力。本文将探讨中国人工智能的历史、现状&#xff0c;并展望其未来发展。 人工智能的起源与历史 人工智能的概念最早诞生于1956年的美国达特茅斯学院的夏季研讨会…

【数据库】数据库元素的层次,树形结构的下的多粒度加锁,以及幻象的正确处理

数据库元素的层次 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会定期…

(学习笔记)Xposed模块编写(一)

前提&#xff1a;需要已经安装Xposed Installer 1. 新建一个AS项目 并把MainActvity和activity_main.xml这两个文件删掉&#xff0c;然后在AndriodManifest.xml中去掉这个Activity的声明 2. 在settings.gralde文件中加上阿里云的仓库地址&#xff0c;否则Xposed依赖无法下载 m…

【llm使用】ChatGLM3-6B Transformers部署调用

文章目录 环境准备模型下载代码准备部署 说明&#xff1a;本文转自国内开源组织datawhale的repo&#xff1a; self-llm 环境准备 在autodl平台中租一个3090等24G显存的显卡机器&#xff0c;如下图所示镜像选择PyTorch–>2.0.0–>3.8(ubuntu20.04)–>11.8 接下来打…

windows 系统读取 Linux Ext 分区硬盘

windows 系统读取 Linux Ext 分区硬盘 起因是需要处理一些在硬盘中的数据&#xff0c;硬盘插到我的电脑上后无法读盘&#xff0c;在 Windows 存储管理器中查看显示未分配&#xff0c;需要格式化&#xff0c;但是在 Ubuntu 系统的电脑中可以正常识别&#xff0c;试验了几次后发…

.NET8构建统计Extreme Optimization Numerical Libraries

为 .NET 8 构建统计应用程序 Extreme Optimization Numerical Libraries for .NET V8.1.22 添加了对 .NET 8 的支持&#xff0c;使您可以使用最新版本的 Microsoft 平台。 Extreme Optimization Numerical Libraries for .NET 是通用数学和统计类的集合&#xff0c;为技术和统计…

【Linux】第二十六站:软硬链接

文章目录 一、软链接二、硬链接三、ln命令四、该如何理解硬链接&#xff1f;五、如何理解软链接六、为什么要用软硬链接1.软链接的应用场景2.硬链接的应用场景 一、软链接 如下所示&#xff0c;我们创建一个文件以后&#xff0c;然后执行下面的指令 ln -s file.txt soft-link…

C++模拟实现unordered_map和unordered_set

目录 1.了解哈希表 1.哈希表 1.他的实现原理就是&#xff1a; ​编辑 2.写单个数据的类型&#xff08;这边先模拟map的kv类型&#xff0c;后面会再一起改&#xff0c;这边先一步步的先简单实现他&#xff09; 3.封装整个类&#xff1a; 4.哈希表中存储string 2.哈…

前后端分离部署https

引用&#xff1a;https://blog.csdn.net/weixin_35676679/article/details/127841598 前后端部署&#xff0c;&#xff0c;一般用的是nginx和java&#xff0c;&#xff0c;&#xff0c; 下载SSL证书&#xff1a; java配置https 将证书配置到springboot中 server:port: 544…

线程...

文章目录 1.Linux中线程该如何理解2.重新定义线程 和 进程3.重谈地址空间 --- 第四讲4.Linux线程周边的概念 线程:是进程内的一个执行分支。线程的执行粒度,要比进程要细 很多教材喜欢这么说&#xff0c;这只是一个线程的特征之一&#xff0c;来解释线程。 1.Linux中线程该如何…

基于51单片机的交通灯_紧急开关+黄灯倒计时+可调时间

51单片机交通灯_紧急开关黄灯倒计时可调时间 开题报告系统硬件设计主控制器选择系统硬件结构图时钟及复位电路指示灯及倒计时模块 倒计时模块&#xff1a;程序软件主流程框架main函数 设计报告资料清单资料下载链接 基于51单片机交通灯_紧急开关黄灯倒计时可调时间 仿真图prote…

题目:神奇的进制

解题思路&#xff1a; 用电脑自带的计算器&#xff0c;切换到程序员模式。里面有进制转换功能。 由题目&#xff0c;要求严格递增且都为字母&#xff0c;还要大于2023&#xff0c;则数字16进制为ABC。

【STM32】电机驱动

一、电机分类 二、直流电机的分类 1.有刷电机 2.无刷电机 3.直流减速电机 三、H桥电路 正向旋转 驱动Q1和Q4 反向旋转 驱动Q2和Q3 四、MC3386电机驱动芯片 1.基本原理图 1&#xff09;前进/后退&#xff1a;IN1和IN2的电平顺序决定电机的正反转 2&#xff09;调节速度&#…

Redis对象

Redis根据基本数据结构构建了自己的一套对象系统。主要包括字符串对象、列表对象、哈希对象、集合对象和有序集合对象 同时不同的对象都有属于自己的一些特定的redis指令集&#xff0c;而且每种对象也包括多种编码类型&#xff0c;和实现方式。 Redis对象结构 struct redisOb…

Linux 防火墙

目录 安全技术 防火墙的分类 按保护范围划分 按实现方式划分 按网络协议划分 应用层防火墙&#xff08;7层&#xff09; 防火墙的工作原理 linux防火墙的基本认识 防火墙工具介绍 1.iptables 2.firewalld 3.nftables 安全技术 —— 入侵检测系统&#xff08;Intru…

centos7中通过kubeadmin安装k8s集群

k8s部署官方提供了kind、minikube、kubeadmin等多种安装方式。 其中minikube安装在之前的文章中已经介绍过&#xff0c;部署比较简单。下面介绍通过kubeadmin部署k8s集群。 生产中提供了多种高可用方案&#xff1a; k8s官方文档 本文安装的是1.28.0版本。 建议去认真阅读一下…

使用coco数据集进行语义分割(1):数据预处理,制作ground truth

如何coco数据集进行目标检测的介绍已经有很多了&#xff0c;但是关于语义分割几乎没有。本文旨在说明如何处理 stuff_train2017.json stuff_val2017.json panoptic_train2017.json panoptic_val2017.json&#xff0c;将上面那些json中的dict转化为图片的label mask&am…

Docker下安装MySQL

如果在Docker下直接拉取MySQL并运行镜像&#xff0c;由于没有指定字符编码集&#xff0c;可能会存在插入中文出现乱码的情况&#xff0c;并且当容器删除后&#xff0c;容器里面存在的数据会丢失&#xff0c;所以在运行容器时应该使用数据卷进行挂载&#xff0c;按照如下步骤操作…

大模型中幂律缩放法则和涌现能力

幂律缩放法则是一种用于描述两个变量之间关系的数学模型。 根据幂律缩放法则&#xff0c;当一个变量的值变化时&#xff0c;另一个变量的值以指数方式变化。具体而言&#xff0c;幂律缩放法则可以表示为Y a * X^b&#xff0c;其中Y表示一个变量的值&#xff0c;X表示另一个变…