MySQL 窗口函数温故知新

本文用于复习数据库窗口函数,希望能够温故知新,也希望读到这篇文章的有所收获。

本文以:MySQL为例

参考文档: https://www.begtut.com/mysql/mysql-window-functions.html

使用的样例数据:https://www.begtut.com/mysql/mysql-sample-database.html


1. 概括的说明
函数说明
ROW_NUMBER为其分区中的每一行分配一个序号。
RANK根据ORDER BY的字段,为每一行分配一个排名。 值相同的行分配相同的排名, 下一行排名不联系,会累加值相同的行数。
DENSE_RANK与RANK()函数类似,只是当出现值相同的行时,排名是连续的,不是累加行数。
PERCENT_RANK计算分区或结果集中行的百分位数。计算公式为:(当前从小到大排序序号-1 ) / (总序号数-1) 【就是(rank - 1) / (total_rows - 1) 】
FIRST_VALUE返回指定表达式相对于窗口框架中第一行的值。
LAST_VALUE返回指定表达式相对于窗口框架中最后一行的值。
LEAD返回分区中当前行之后的第N行的值。 如果不存在后续行,则返回NULL。
LAG返回分区中当前行之前的第N行的值。 如果不存在前一行,则返回NULL。
NTILE将每个窗口分区的行分配到指定数量的已排名组中。 (把结果分成n个组)
CUME_DIST计算一组值中值的累积分布。
NTH_VALUE返回窗口框架第N行的参数

2.  注意 rows between 的用法

  • rows between …… and ……
  • unbounded preceding 前面所有行 、n preceding  前面n行
  • unbounded following 后面所有行 、n following  后面n行
  • current row 当前行
SELECTorderNumber,productCode,quantityOrdered,SUM(quantityOrdered) OVER(PARTITION BY orderNumber ORDER BY productcode) AS quantity_amount,-- 前面一行和当前行的值累加SUM(quantityOrdered) OVER(PARTITION BY orderNumber ORDER BY productcode ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) quantity_add
FROMmysqldemo.orderdetails
WHEREorderNumber = 10103;
3. 注意 range between的用法

range between 按照排序字段的值限制窗口大小。主要将order by后面字段排序后,然后根据排序字段的值,框定一个范围,再对这个范围内的行进行汇总。样例如下:
 

sum(num) over(order by dateTime range between interval 6 day preceding and current row) 
-- 必须是date类型的数据,这一天和前面6天(如果存在)的数据sum(close) over(order by salary range between 100 preceding and 200 following) 
--通过 salary 字段差值来进行选择。如当前行的 salary 字段值是 200,那么这个窗口大小的定义就会选择分区中 salary 字段值落在 100 至 400 区间的记录(行),再求这些行的sum(close).

需要注意的点:

  • rows表示行,就是前n行,后n行。
  • range表示的是具体的值,比这个值小n的行,比这个值大n的行。是以当前值为锚点进行计算。
  • 同时 range 也可以使用 between unbounded preceding and unbounded following,效果和等同于rows一样,取上下限所有行,不指定值。
  • range 窗口仅对数字和日期起作用,因为需要计算值的范围。
  • 在range 的开窗中,order by 中只能有一列;rows 的开窗的order by 可以有多列。
SELECTorderNumber,productCode,quantityOrdered,SUM(quantityOrdered) OVER(PARTITION BY orderNumber ORDER BY quantityOrdered) AS quantity_amount,-- quantityOrdered 的值-1 和 +2的值区间范围内的行的累加SUM(quantityOrdered) OVER(PARTITION BY orderNumber ORDER BY quantityOrdered RANGE BETWEEN 1 PRECEDING AND 2 following) quantity_add
FROMmysqldemo.orderdetails;

4. ROW_NUMBER & RANK & DENSE_RANK

比较常用,都很熟悉,基本用法就不用赘述了。

SELECTorderNumber,productCode,quantityOrdered,ROW_NUMBER() OVER (ORDER BY quantityOrdered) AS nb,RANK() OVER (PARTITION BY orderNumber ORDER BY quantityOrdered) AS rank_quantity,DENSE_RANK() OVER (PARTITION BY orderNumber ORDER BY quantityOrdered) AS dense_rank_quantity
FROMmysqldemo.orderdetails
WHEREorderNumber = 10103;

rank和dense_rank 的区别,就是遇到有多行值相同时,那么下一行的序号,rank会加上重复的行数,那么rank对应的序号就不连续了;dense_rank 不会加上重复的行数,保持序号任然是连续的。

需要注意的点:

  • ROW_NUMBER 不加partition的时候,对所有行加序号,加partition之后分组加序号。
  • RANK 注意不加order by的时候,不排序,全是1,即使加partition也没用;一定要加order by才会排序。
  • SUM 用法和 ROW_NUMBER 相同,汇总和分组汇总。
SELECT *, ROW_NUMBER() OVER () row_num0,ROW_NUMBER() OVER (PARTITION BY productline) row_num1,RANK() OVER() AS Rank00,RANK() OVER(PARTITION BY productline) AS Rank01,RANK() OVER(PARTITION BY productline,order_year) AS Rank02,RANK() OVER(order by amount) AS Rank1,RANK() OVER(PARTITION BY productline order by amount) AS Rank2,SUM(amount) OVER(PARTITION BY productline,order_year ) AS amount0,SUM(amount) OVER(PARTITION BY productline ) AS amount1,SUM(amount) OVER() AS amount2
FROM (SELECT productline, year(orderDate) order_year, sum(quantityOrdered) as amountFROM ordersINNER JOIN orderdetails USING (orderNumber)INNER JOIN products USING (productCode)GROUP BY productline,order_year) T;

5. PERCENT_RANK()

函数返回一个从0到1的数字。 计算公式为:(rank - 1) / (total_rows - 1)。

rank是当前行的等级,total_rows是要计算的行数。 公式的意思就是计算当前行的等级减1,除以分区或结果集中的总行数减1。

  • PERCENT_RANK()对于分区或结果集中的第一行,函数始终返回零。重复的列值将接收相同的PERCENT_RANK()值。
  • PERCENT_RANK()是一个顺序敏感函数,因此,您应始终使用ORDER BY子句。
CREATE TABLE productLineSales -- 我们创建了一张表,后面还会重复用到它
SELECTproductLine,YEAR(orderDate) orderYear,SUM(quantityOrdered * priceEach) orderValue
FROM orderDetails
INNER JOIN orders USING (orderNumber)
INNER JOIN products USING (productCode)
GROUP BY productLine , YEAR(orderDate); WITH t AS (SELECT productLine, SUM(orderValue) orderValueFROM productLineSalesGROUP BY productLine
)
SELECTproductLine,orderValue,ROUND(PERCENT_RANK() OVER (ORDER BY orderValue),2) percentile_rank
FROM t; 

在这个例子中: 首先,我们使用表达式按产品线汇总订单值。 其次,我们用它PERCENT_RANK()来计算每种产品的订单价值的百分等级。
以下是输出中的一些分析:

  • 订单价值Trains并不比任何其他产品线更好,后者用零表示。
  • Vintage Cars 表现优于50%的其他产品。
  • Classic Cars 表现优于任何其他产品系列,因此其百分比等级为1或100%

6. CUME_DIST

它表示值小于或等于当前行的值除以总行数。 公式为: ROW_NUMBER() / total_rows 。注意和 PERCENT_RANK 的区别。

  • CUME_DIST()函数的返回值大于零且小于或等于1。
  • 重复的列值接收相同的CUME_DIST()值。 

样例:计算某产品的订单订货量数量分布 (注意第7行开始有重复的值31,对应百分比也是相同的。表示数量小于等于31的一共10行,占总行数28的35.71%)
 

SELECT orderNumber, productCode, quantityOrdered, ROW_NUMBER() OVER(ORDER BY quantityordered) AS nb,CUME_DIST() OVER(ORDER BY quantityordered) AS pct,PERCENT_RANK() OVER(ORDER BY quantityordered) AS pct_rank
FROM mysqldemo.orderdetails
WHERE productcode = 'S18_2949';

7. FIRST_VALUE

样例:获取客户首单订单金额。

SELECT customernumber,amount,paymentDate,FIRST_VALUE (amount) OVER (PARTITION BY customernumber ORDER BY paymentDate) AS first_amount
FROM payments
ORDER BY customernumber;

8. Last_Value
样例:获取客户最后一笔订单金额。
注意:Last_Value 和 First_Value 不同, 他认为每一行,是当前行中的最后一行。注意对比下面两个字段的不同。

SELECT customernumber, amount, paymentDate,last_value (amount) OVER (PARTITION BY customernumber ORDER BY paymentDate) AS last_amount,last_value (amount) OVER (PARTITION BY customernumber ORDER BY paymentDate RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS last_amount_umbounded
FROM payments
ORDER BY customernumber;


8. Lead和Lag

函数类似,是查询某一字段的从当前行往后找到第N行的数据(Lead)和往前找到第N行的数据(Lag)。在找到某一行的偏移n行的数据非常有用。
lead/lag(expression, offffset, default) over(partion by ......order by ......)

  • expression 要取的是哪一个字段
  • offset 是从当前行前进(lead)/后退(lag)的行数。 必须是一个非负整数,为零则取当前行。
  • default 如果没有后续行,则函数返回default。例如,如果offset是1,则lead的最后一行,lag的第一行的返回值为default。 未指定default_value,则返回 NULL 。

样例: 查询出上一个订单,下一个订单的时间 

SELECT customerName,orderDate,LEAD(orderDate,1) OVER (PARTITION BY customerNumber ORDER BY orderDate ) nextOrderDate,LAG(orderDate,1) OVER (PARTITION BY customerNumber ORDER BY orderDate ) PreviousOrderDate
FROM orders
INNER JOIN customers USING (customerNumber); 


样例:查询出下单时间间隔最长的用户。

SELECT customerName, MAX(orderdate_interval) AS MAX_interval, RANK() OVER(ORDER BY MAX(orderdate_interval) DESC) AS data_rank
FROM(SELECT customerName,orderDate,LEAD(orderDate,1) OVER (PARTITION BY customerNumber ORDER BY orderDate ) nextOrderDate,datediff(LEAD(orderDate,1) OVER (PARTITION BY customerNumber ORDER BY orderDate), orderDate) orderdate_intervalFROM ordersINNER JOIN customers USING (customerNumber)) T1
WHERE nextOrderDate IS NOT NULL
GROUP BY customerName; 

9. NTILE 平均分组

样例:将产品线按照年份,汇总订单金额,并且划分为三个组。
注意不能平均分配时,例如将9行数据分成4个组,他会把第1组分3个,剩余3个组每个组2个;

SELECTproductline, orderYear, orderValue,NTILE(3) OVER (PARTITION BY orderYear ORDER BY orderValue DESC) product_line_group
FROM productlineSales; 


样例: 查询出2013支付金额排名前30%的所有用户 

SELECT customerNumber, pay_amount, level 
FROM (SELECT customerNumber, SUM(amount) AS pay_amount,NTILE(10) OVER(ORDER BY SUM(amount) DESC) AS levelFROM mysqldemo.paymentsWHERE Year(paymentDate) = 2013GROUP BY customerNumber)a 
WHERE level in (1,2,3);


通过这种方法计算出来的百分比不准确,通过下面的SQL,会发现前3个组的人数超过了30%。

SELECT COUNT(customerNumber), level
FROM (SELECT customerNumber, SUM(amount) AS pay_amount,NTILE(10) OVER(ORDER BY SUM(amount) DESC) AS levelFROM mysqldemo.paymentsWHERE Year(paymentDate) = 2013GROUP BY customerNumber)a 
GROUP BY level;


使用用 CUME_DIST 效果更好。

SELECT customerNumber, pay_amount, level, pct
FROM (SELECT customerNumber, SUM(amount) AS pay_amount,NTILE(10) OVER(ORDER BY SUM(amount) DESC) AS level,CUME_DIST() over(order by SUM(amount) desc) as pctFROM mysqldemo.paymentsWHERE Year(paymentDate) = 2013GROUP BY customerNumber)a 

直接定位带排序小于等于30%的即可。从结果可以看出,和NTILE不一样,第三组的人没有全部都取。 

10. NTH_VALUE

函数格式为:

NTH_VALUE(expression, N)
OVER (partition_clauseorder_clauseframe_clause) 

从有序行集中的第N行获取值;如果第N行不存在,则函数返回NULL;N必须是正整数。
注意:From First(标准SQL 支持 From Last, MySQL只支持From First。如果要模拟效果From Last,则可以使用其中ORDER BY倒叙排列)
样例:2015年每月购买金额第三的人

SELECT paymentmonth,customernumber, amount, 
NTH_VALUE(customernumber, 3) OVER(PARTITION BY paymentmonth ORDER BY amount DESC RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS NTH
FROM (SELECT MONTH(paymentDate) AS paymentmonth, customernumber,  SUM(amount) amountFROM paymentsWHERE YEAR(paymentDate) = 2015GROUP BY customernumber, paymentmonth) T1

11. 测试:查询出每年连续下单的客户和连续的年份
方法1

使用 lag 取上一年的年份,计算差值是1的,就是这两年是连续的;然后对customerName进行group by。

SELECT customerName, max(orderYear), min(previousYear), SUM(gap)+1
FROM (SELECT customerName, orderYear, lag(orderYear) over(partition by customerName order by orderYear) AS previousYear,orderYear - lag(orderYear) over(partition by customerName order by orderYear)  gapFROM (SELECT customerName,YEAR(orderDate) AS orderYearFROM ordersINNER JOIN customers USING (customerNumber)GROUP BY customerName, orderYear ) T1) T2
WHERE gap =1
GROUP BY customerName

方法2

用Year 减去row_number, 取得gap,gap相同的,就是年份连续的。

SELECT customerName, minYear, maxYear, max(nb)
FROM (SELECT customerName,gap,orderYear, min(orderYear) OVER (partition by customerName,gap ORDER BY customerName,gap) minYear, max(orderYear)OVER (partition by customerName,gap ORDER BY customerName,gap) maxYear,ROW_NUMBER() OVER (partition by customerName,gap ORDER BY customerName,gap) nbFROM(SELECT customerName, orderYear, orderYear-nbbycustomer as gapFROM (SELECT customerName,YEAR(orderDate) AS orderYear,ROW_NUMBER() OVER (PARTITION BY customerName ORDER BY YEAR(orderDate)) nbbycustomerFROM ordersINNER JOIN customers USING (customerNumber)GROUP BY customerName,orderYear)T1) T2
) T3
WHERE minYear <> maxYear
GROUP BY customerName, minYear, maxYear;
12. 其它有趣的函数

使用rand() 获取随机10行数据。

select * from customers order by rand() limit 10;

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

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

相关文章

9.vue学习笔记(组件传递Props校验+组件事件-组件传递数据+组件事件-配合“v-model”使用)

文章目录 1.组件传递Props校验1.1.默认值1.2.必选项1.3.注意事项&#xff1a;props 是只读的 2.组件事件-组件传递数据2.1.温馨提示&#xff1a;组件之间传递数据的方案 3.组件事件-配合“v-model”使用 1.组件传递Props校验 Vue组件可以更细致地声明对传入的 props 的校验要求…

学习鸿蒙一定要搞清楚的几个概念

目录 1、UI框架 2、应用模型 2.1、应用模型介绍 2.2、两种应用模型 2.3、应用模型和UI框架的关系 3、Ability 3.1、Ability介绍 3.2、FA模型的ability 3.3、Stage模型的Ability 1、UI框架 HarmonyOS提供了一套UI(User Interface,用户界面)开发框架&#xff0c;即方舟…

java 课程签到管理系统Myeclipse开发mysql数据库web结构jsp编程servlet计算机网页项目

一、源码特点 java 课程签到管理系统是一套完善的java web信息管理系统 采用serlvetdaobean&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0…

OpenGauss数据库本地搭建并结合内网穿透实现远程访问

文章目录 前言1. Linux 安装 openGauss2. Linux 安装cpolar3. 创建openGauss主节点端口号公网地址4. 远程连接openGauss5. 固定连接TCP公网地址6. 固定地址连接测试 前言 openGauss是一款开源关系型数据库管理系统&#xff0c;采用木兰宽松许可证v2发行。openGauss内核深度融合…

专业140+总分420+南京信息工程大学811信号与系统考研经验南信大电子信息与通信工程,真题,大纲,参考书

今年顺利被南信大电子信息录取&#xff0c;初试420&#xff0c;专业811信号与系统140&#xff08;Jenny老师辅导班上140很多&#xff0c;真是大佬云集&#xff09;&#xff0c;今年应该是南信大电子信息最卷的一年&#xff0c;复试线比往年提高了很多&#xff0c;录取平均分380…

来分析两道小题

一、源码 二、分析 首先它会接两个参数一个是id一个是ps&#xff0c;传递的话会包含一个flag.php&#xff0c;然后数据库连接&#xff0c;之后传递过滤&#xff0c;然后查询&#xff0c;如果查到了就会取id&#xff0c;取出来看是不是跟adog一样&#xff0c;如果是它告诉你账号…

会声会影2024新功能及剪辑视频步骤教程

会声会影2024的新功能主要包括&#xff1a; 全新的标题动态与特效&#xff1a;用户可以为文字标题指定进入、中场和退出的不同动态效果&#xff0c;比如闪现进入、中场弹跳和淡出退出等&#xff0c;让文字标题更具动感。此外&#xff0c;还新增了多个标题特效&#xff0c;包括…

软考-中级-系统集成2023年综合知识(一)

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 软考中级专栏回顾 专栏…

由面试题“Redis是否为单线程”引发的思考

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; &#x1f40b; 希望大家多多支…

【大数据】Flink 之部署篇

Flink 之部署篇 1.概述和参考架构2.可重复的资源清理3.部署模式3.1 Application 模式3.2 Per-Job 模式&#xff08;已废弃&#xff09;3.3 Session 模式 Flink 是一个多用途框架&#xff0c;支持多种不同的混合部署方案。下面&#xff0c;我们将简要介绍 Flink 集群的构建模块、…

爬虫基础(下)

requests模块可以用来获取网络数据&#xff1b; 那么对于爬虫来说&#xff0c;要获取下图网页中的内容&#xff0c;就需要网页的URL。 复制链接方法是&#xff0c;打开网页&#xff0c;点击链接框&#xff0c;右键选择复制。 requests.get()函数可用于模拟浏览器请求网页的过…

Flutter 3.19.0 版本新特性

其实在每个版本的更新中呢&#xff0c;都会合并很多很多的这个合并请求、还有开发建议&#xff0c;那么本版本的也不例外&#xff0c;社区官方发布的公告是合并了168个社区成员的1429个拉请求。 当然&#xff0c;如果你的时间允许的话&#xff0c;你可以去查看一下这些请求&am…

C#,整数转为短字符串(Short string)的加解密算法与源代码

1 整数转为短字符串的应用 网站生成的动态 URL 往往以内容序列号id为标识与参数&#xff0c;比如&#xff1a; http://www.jerry.com/tom.aspx?id1 使用 Web Rewrite&#xff0c;可以实现网页静态化&#xff0c;称为&#xff1a; http://www.jerry.com/content/1.html 对…

HQYJ 2024-2-22 作业

复习前面知识点(指针、结构体、函数)&#xff08;已完成&#xff09;整理思维导图&#xff08;已完成&#xff09;顺序表(按位置插入、按位置删除和去重、重新写)理解链表的代码&#xff0c;尝试写一下链表的尾插和输出 3.顺序表(按位置插入、按位置删除和去重、重新写) 按位置…

如何使用Docker本地部署Jupyter+Notebook容器并结合内网穿透实现远程访问

文章目录 1. 选择与拉取镜像2. 创建容器3. 访问Jupyter工作台4. 远程访问Jupyter工作台4.1 内网穿透工具安装4.2 创建远程连接公网地址4.3 使用固定二级子域名地址远程访问 本文主要介绍如何在Ubuntu系统中使用Docker本地部署Jupyter Notebook&#xff0c;并结合cpolar内网穿透…

数据结构与算法之美学习笔记:53 | 算法实战(二):剖析搜索引擎背后的经典数据结构和算法

目录 前言整体系统介绍搜集1. 待爬取网页链接文件&#xff1a;links.bin2. 网页判重文件&#xff1a;bloom_filter.bin3. 原始网页存储文件&#xff1a;doc_raw.bin4. 网页链接及其编号的对应文件&#xff1a;doc_id.bin 分析索引查询总结引申 前言 本节课程思维导图&#xff1…

【GStreamer】GstElement详解:GStreamer 中最重要的对象

1、什么是元素GstElement? 每个解码器、编码器、解复用器、视频或音频输出实际上都是一个GstElement。GstElement可以视为一个黑盒子:例如,对于解码器元素,输入为已编码数据,输出为解码后的数据,解码过程已由GstElement封装好。 2、都有哪些元素GstElement? 2.1 源点…

嵌入式学习-qt-Day2

嵌入式学习-qt-Day2 一、思维导图 二、作业 1.使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 2.将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账…

推荐一个内网穿透工具,支持Windows桌面、Linux、Arm平台客户端

神卓互联是一款常用的内网穿透工具&#xff0c;它可以将本地服务器映射到公网上&#xff0c;并提供域名或子域名给外部访问。神卓互联具有简单易用、高速稳定的特点&#xff0c;支持Windows桌面版、Linux版、Arm版客户端&#xff0c;以及硬件等。 神卓互联内网穿透技术简介 企…

【C语言】Debian安装并编译内核源码

在Debian 10中安装并编译内核源码的过程如下&#xff1a; 1. 安装依赖包 首先需要确保有足够的权限来安装包。为了编译内核&#xff0c;需要有一些基础的工具和库。 sudo apt update sudo apt upgrade sudo apt install build-essential libncurses-dev bison flex libssl-d…