MySQL窗口函数——让查询变得更简单

news2024/11/27 16:25:51

文章目录

  • 一、窗口函数概述
    • 1、什么是窗口函数
    • 2、窗口函数有哪些
      • (1)聚合函数(聚合函数不是本文讨论的重点)
      • (2)专用窗口函数
    • 3、基本语法
    • 4、测试数据准备
  • 二、窗口函数使用
    • 1、初识窗口函数:使用聚合函数
    • 2、序号函数:ROW_NUMBER()、RANK()、DENSE_RANK()
    • 3、分布函数:PERCENT_RANK()、CUME_DIST()
    • 4、前后函数
    • 5、头尾函数:FIRST_VALUE()、LAST_VALUE()
    • 6、其他函数:NTH_VALUE()、NTILE()
  • 三、窗口函数的命名
    • 1、语法格式
    • 2、使用示例
  • 四、窗口函数框架
  • 参考资料

一、窗口函数概述

1、什么是窗口函数

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

窗口的意思是将数据进行分组,每个分组即是一个窗口,这和使用聚合函数时的group by分组类似,但与聚合函数不同的地方是:
聚合函数(例如:sum/avg/min/max)会针对每个分组(窗口)聚合出一个结果(每一组返回一个结果)。
窗口函数会对每一条数据进行计算,并不会使返回的数据变少(每一行返回一个结果)。

2、窗口函数有哪些

窗口函数可以分为两类:
一类既可以做为聚合函数,也可以作为窗口函数,当函数单独使用时是聚合函数,当与over关键字同时使用时作为窗口函数。
另一类是专用窗口函数,他们必须与 over 关键字同时使用。

(1)聚合函数(聚合函数不是本文讨论的重点)

MySQL聚合函数详解——让查询变得很简单

  • AVG() 返回自变量的平均值
  • BIT_AND() 返回按位AND
  • BIT_OR() 返回按位或
  • BIT_XOR() 返回按位异或
  • COUNT() 返回返回的行数的计数
  • COUNT(DISTINCT) 返回多个不同值的计数
  • GROUP_CONCAT() 返回串联的字符串
  • JSON_ARRAYAGG() 将结果集作为单个JSON数组返回
  • JSON_OBJECTAGG() 将结果集作为单个JSON对象返回
  • MAX() 返回最大值
  • MIN() 返回最小值
  • STD() 返回总体标准差
  • STDDEV() 返回总体标准差
  • STDDEV_POP() 返回总体标准差
  • STDDEV_SAMP() 返回样本标准偏差
  • SUM() 归还总数
  • VAR_POP() 返回总体标准方差
  • VAR_SAMP() 返回样本方差
  • VARIANCE() 返回总体标准方差

(2)专用窗口函数

序号函数:

  • row_number() 顺序排序:对数据中的序号进行顺序显示,不管其排序结果是否出现重复值,排序结果为1,2,3,4,5…
  • rank() 并列排序:相同字段数值并列排序,且跳过重复序号,如, 1,1,3,4,5 。rank函数没有参数,但需要指定按照那个字段进行排名,所以使用rank函数必须用order by参数,order by的排序字段就是排名字段
  • dense_rank() 并列排序:相同字段数值并列排序,且不跳过重复序号,如:1,1,2,3,4

分布函数:

  • percent_rank() 累计百分比。函数计算结果为:小于该条记录值的所有记录的行数/该分组的总行数-1,所以该记录的返回值为[0,1]。和之前的RANK()函数相关,每行按照如下公式进行计算: (rank - 1) / (rows - 1) 其中,rank为RANK()函数产生的序号,rows为当前窗口的记录总行数。
  • cume_dist() 累计分布值。分组值小于等于当前值的行数与分组总行数的比值 ,(0,1]。 分组内大于等于当前rank值的行数/分组内总行数。(常用)

前后函数:

  • lag(expr,n) 返回当前行的前n行的expr的值
  • lead(expr,n) 返回当前行的后n行的expr的值

头尾函数:

  • first_value(expr) 返回第一个expr的值
  • last_value(expr) 返回最后一个expr的值

其他函数:

  • nth_value(expr,n) 返回第n个expr的值
  • ntile(n) 将分区中的有序数据分为n个桶,记录桶的编号

3、基本语法

select 窗口函数 over (partition by 用于分组的列名, order by 用于排序的列名)

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

4、测试数据准备

CREATE TABLE `student` (
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '姓名',
  `course` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '课程',
  `score` int DEFAULT NULL COMMENT '分数'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('张三', '语文', 90);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('张三', '数学', 80);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('张三', '英语', 80);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('张三', '历史', 85);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('张三', '物理', 86);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('张三', '化学', 88);

INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('李四', '语文', 90);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('李四', '数学', 88);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('李四', '英语', 85);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('李四', '历史', 82);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('李四', '物理', 70);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('李四', '化学', 75);

INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('王五', '语文', 88);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('王五', '数学', 88);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('王五', '英语', 83);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('王五', '历史', 83);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('王五', '物理', 80);
INSERT INTO `athena_opencourse`.`student`(`name`, `course`, `score`) VALUES ('王五', '化学', 82);

二、窗口函数使用

1、初识窗口函数:使用聚合函数

通常来说,我们写一个聚合函数,会将分组内的数据进行聚合,形成一行。而窗口操作不会将多组查询行折叠成单个输出行。相反,它们为每一行产生一个结果:

SELECT 
	name,
	course,
	sum(score)
FROM
	student
group by course order by score desc;

SELECT 
	name,
	course,
	score,
	sum(score) OVER(PARTITION BY course ORDER BY score desc)
FROM
	student
;

由结果我们可以看出,窗口函数会逐行渲染数据,每一行数据不会合并,而是一行一行的累加:
在这里插入图片描述

SELECT 
	name,
	course,
	score,
	sum(score) OVER w 'sum',
	avg(score) OVER w 'avg',
	count(score) OVER w 'count',
	max(score) OVER w 'max',
	min(score) OVER w 'min'
FROM
	student
	window w AS (PARTITION BY course ORDER BY score desc)
;

在这里插入图片描述

2、序号函数:ROW_NUMBER()、RANK()、DENSE_RANK()

序号函数中,ORDER BY影响行的编号顺序。没有ORDER BY,行编号是不确定的。

  • row_number() 顺序排序:对数据中的序号进行顺序显示,不管其排序结果是否出现重复值,排序结果为1,2,3,4,5…
  • rank() 并列排序:相同字段数值并列排序,且跳过重复序号,如, 1,1,3,4,5 。rank函数没有参数,但需要指定按照那个字段进行排名,所以使用rank函数必须用order by参数,order by的排序字段就是排名字段
  • dense_rank() 并列排序:相同字段数值并列排序,且不跳过重复序号,如:1,1,2,3,4
SELECT
	name,
	course,
	score,
	ROW_NUMBER() OVER(partition by course order by score desc) AS 'row_number',
	RANK() OVER(partition by course order by score desc)  AS 'rank',
	DENSE_RANK() OVER(partition by course order by score desc)  AS 'dense_rank' 
FROM
	student;

根据结果我们很明显的可以看出这三个函数的区别,虽然都是用于产生序号,用法稍微有一些区别。
在这里插入图片描述

通过使用序号函数,我们可以很轻松的获取分组内前几条数据:

select * from (
SELECT
	name,
	course,
	score,
	ROW_NUMBER() OVER(partition by course order by score desc) AS 'row_number'
FROM
	student) tmp 
where tmp.row_number < 3;

在这里插入图片描述

3、分布函数:PERCENT_RANK()、CUME_DIST()

  • percent_rank() 累计百分比。函数计算结果为:小于该条记录值的所有记录的行数/该分组的总行数-1,所以该记录的返回值为[0,1]。和之前的RANK()函数相关,每行按照如下公式进行计算: (rank - 1) / (rows - 1) 其中,rank为RANK()函数产生的序号,rows为当前窗口的记录总行数。
  • cume_dist() 累计分布值。分组值小于等于当前值的行数与分组总行数的比值 ,(0,1]。 分组内大于等于当前rank值的行数/分组内总行数。(常用)
SELECT
	name,
	course,
	score,
	RANK() OVER(partition by course order by score desc)  AS 'rank',
	PERCENT_RANK() OVER(partition by course order by score desc)  AS 'percent_rank' ,
	CUME_DIST() OVER(partition by course order by score desc)  AS 'cume_dist'
FROM
	student;

由结果我们可以看出,PERCENT_RANK就是统计小于该值的比例,也就是排名的百分比。CUME_DIST()函数主要用于查询小于或等于该值的比例。
在这里插入图片描述

4、前后函数

  • lag(expr,n) 返回当前行的前n行的expr的值
  • lead(expr,n) 返回当前行的后n行的expr的值

LAG() 函数用于在查询结果中访问当前行之前的行的数据。它允许您检索前一行的值,并将其与当前行的值进行比较或计算差异。LAG()函数对于处理时间序列数据或比较相邻行的值非常有用。LAG()函数完整的表达式为 LAG(column, offset, default_value),包含三个参数:
column:就是列名,获取哪个列的值就是哪个列名,很好理解。
offset: 就是向前的偏移量,取当前行的前一行就是1,前前两行就是2。
default_value:是可选值,如果向前偏移的行不存在,就取这个默认值。

SELECT
	name,
	course,
	score,
	-- LAG默认直接显示上一个的值,可用于查看与上一个值的变化
	LAG(score) OVER w AS 'LAG',
	score - LAG(score) OVER w  AS 'LAG2',
	-- LEAD默认直接显示下一个的值,可用于查看与下一个值的变化
	LEAD(score) OVER w  AS 'LEAD',
	score - LEAD(score) OVER w  AS 'LEAD2'
FROM
	student
	window w as (partition by course order by score desc)
	;

我们通过结果也可以看出,LAG显示该值与上一个值的变化(默认n为1),LEAD正好相反显示该值与下一个值的变化。常用于计算上一个值与下一个值的分数差,也可以用于统计两次请求之间相差的时间等等。

细心的小伙伴也发现了,此处我们的sql有一些变化。没错,window函数可以在最后进行命名,复用起来更加方便,后续我们会详细介绍。
在这里插入图片描述

5、头尾函数:FIRST_VALUE()、LAST_VALUE()

FIRST_VALUE(expr)函数返回第一个expr的值。
LAST_VALUE(expr)函数返回最后一个expr的值。

SELECT
	name,
	course,
	score,
	FIRST_VALUE(score) OVER w AS 'first',
	LAST_VALUE(score) OVER w  AS 'last'
FROM
	student
	window w as (partition by course order by score desc)
	;

从结果看,我们对FIRST_VALUE()很清晰,就是获取的第一个值,但是LAST_VALUE()获取的值跟我们想象中的不太一样呢?
没错,LAST_VALUE()是获取的框架中的最后一个值,这里引入了一个重要概念:框架(frame),框架是一个动态的概念,是组的子集,从LAST_VALUE函数可以更好看出框架的动态变化,也就是说,LAST_VALUE()获取的是截止当前行的框架内最后一个值(就是当前行自己),而不是整个组的最后一个值
在这里插入图片描述

6、其他函数:NTH_VALUE()、NTILE()

  • nth_value(expr,n) 返回第n个expr的值
  • ntile(n) 将分区中的有序数据分为n个桶,记录桶的编号
SELECT 
	name,
	course,
	score,
	nth_value( score, 2 ) over w 框架内第二个值,
	nth_value( score, 3 ) over w 框架内第三个值
FROM
	student window w AS ( PARTITION BY course ORDER BY score DESC );

获取框架内指定的值:
在这里插入图片描述

SELECT 
	name,
	course,
	score,
	NTILE(2) over w '分组'
FROM
	student window w AS ( PARTITION BY course ORDER BY score DESC );

将框架内数据再分成2组,展示所分的组:
在这里插入图片描述

三、窗口函数的命名

1、语法格式

WINDOW window_name AS (window_spec)
    [, window_name AS (window_spec)] ...

window_spec表达式的格式:
    [window_name] [partition_clause] [order_clause] [frame_clause]

2、使用示例

下面的例子我们同时使用了同一个window,但是写起来非常啰嗦:

SELECT
  val,
  ROW_NUMBER() OVER (ORDER BY val) AS 'row_number',
  RANK()       OVER (ORDER BY val) AS 'rank',
  DENSE_RANK() OVER (ORDER BY val) AS 'dense_rank'
FROM numbers;

使用以下命令可以更简单地编写查询WINDOW定义一次窗口并在OVER 使用:

SELECT
  val,
  ROW_NUMBER() OVER w AS 'row_number',
  RANK()       OVER w AS 'rank',
  DENSE_RANK() OVER w AS 'dense_rank'
FROM numbers
WINDOW w AS (ORDER BY val);

以下使用也是可以的,最终将PARTITION 与ORDER BY部分合并:

SELECT
  DISTINCT year, country,
  FIRST_VALUE(year) OVER (w ORDER BY year ASC) AS first,
  FIRST_VALUE(year) OVER (w ORDER BY year DESC) AS last
FROM sales
WINDOW w AS (PARTITION BY country);

但是要注意以下的场景:

-- 可以这样用,因为窗口定义和引用OVER子句不包含相同种类的属性:
OVER (w ORDER BY country)
... WINDOW w AS (PARTITION BY country)

-- 不能这样用,因为OVER子句指定PARTITION BY对于已经具有的命名窗口PARTITION BY:
OVER (w PARTITION BY year)
... WINDOW w AS (PARTITION BY country)

-- 可以这样用,它包含向前和向后引用,但不包含循环:
WINDOW w1 AS (w2), w2 AS (), w3 AS (w1)

-- 不能这样用,因为它包含一个循环:
WINDOW w1 AS (w2), w2 AS (w3), w3 AS (w1)

四、窗口函数框架

参考:
https://dev.mysql.com/doc/refman/8.0/en/window-functions-frames.html
https://blog.csdn.net/frostlulu/article/details/130729113

参考资料

https://blog.csdn.net/CaiJin1217/article/details/129155992
https://blog.csdn.net/frostlulu/article/details/130729113

https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html
https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html

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

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

相关文章

微头条项目实战:新增RequestHeader注解

1、RequestHeader package com.csdn.mymvc.annotation; import java.lang.annotation.*; Target(ElementType.PARAMETER) Retention(RetentionPolicy.RUNTIME) Inherited public interface RequestHeader { }2、DispatcherServlet package com.csdn.mymvc.core; import com.csd…

ssm+vue的项目管理平台(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的项目管理平台&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 项目介绍…

list部分接口模拟实现(c++)

List list简介list基本框架list构造函数list_node结构体的默认构造list类的默认构造 push_back()iteartor迭代器迭代器里面的其他接口const迭代器通过模板参数实现复用operator->() insert()erase()clear()析构函数迭代器区间构造拷贝构造operator() list简介 - list可以在…

用友NC Cloud accept.jsp接口任意文件上传漏洞复现 [附POC]

文章目录 用友NC Cloud accept.jsp接口任意文件上传漏洞复现 [附POC]0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 用友NC Cloud accept.jsp接口任意文件上传漏洞复现 [附POC] 0x01 前言 免责声明&#xff1a…

RVS—面向目标硬件的软件性能测试工具

产品概述 Rapita Verification Suite&#xff08;简称&#xff1a;RVS&#xff09;&#xff0c;为美国Danlaw公司提供的一款嵌入式系统在板测试套件&#xff0c;主要应用于汽车领域。其产品符合ISO-26262、DO178B/C、IEC-61508等行业标准&#xff0c;兼容Vxworks、Linux、SYSBI…

FTP链接如何直接打开不输密码

发布版本时&#xff0c;通过分享链接给负责生产的同事遇到如下较为麻烦的小问题 打开公司FTP链接&#xff1a; ftp://192.168.70.46/Rtos_5/FF615/3602/product/ 会跳出如下弹窗提示输入登录密码&#xff0c;这样对方还需要手动输入密码才能把包下下来&#xff1a; 通过直接给…

【ATTCK】MITRE ATTCK 设计与哲学

MITRE ATT&CK™:设计与哲学 来源&#xff1a;MITRE ATT&CK™: Design and Philosophy 摘要 MITRE ATT&CK知识库描述了网络对手的行为&#xff0c;并为攻击和防御提供了一个通用的分类。它已成为跨许多网络安全领域的一个有用工具&#xff0c;用于传递威胁情报&…

【干货】132道最新K8S面试题汇总~

k8s全称kubernetes&#xff0c;这个名字大家应该都不陌生&#xff0c;k8s是为容器服务而生的一个可移植容器的编排管理工具&#xff0c;越来越多的公司正在拥抱k8s&#xff0c;并且当前k8s已经主导了云业务流程&#xff0c;推动了微服务架构等热门技术的普及和落地&#xff0c;…

日常交流没有障碍,听力就一定正常吗?

你卖灯笼啊&#xff1f; 对啊对啊&#xff0c;我耳朵聋&#xff01; 你这灯笼多少钱呀&#xff1f; 我耳朵聋了好几年啦&#xff01; 这是个笑话 当然也可以从中看出 听力障碍对一个人日常生活的影响 日常交流没障碍 就是听力正常了吗&#xff1f; 首先我们要了解&#xf…

6大顶级团队计划目标管理软件盘点,全行业适用!

在快节奏的现代工作环境中&#xff0c;高效的团队计划和执行是团队取得成功的关键。然而&#xff0c;随着团队规模不断增大、工作任务不断增加&#xff0c;如何提高团队计划与效率成为了一个挑战。幸运的是&#xff0c;有许多先进的软件工具可以帮助团队更好地组织、协调和追踪…

关于c++中数据sqrt() 精度问题

情景介绍 今天在做一个算法题目的时候&#xff0c;发现&#xff0c;当使用sqrt()方法进行开方的时候&#xff0c;一直存在提交不通过的情况。 问题分析 对数据不断分析后&#xff0c;发现对35进行开方后&#xff0c;仍然满足条件&#xff0c;这就存在问题。 sqrt(35) 5.9160…

SOLIDWORKS软件提供了哪些特征造型方法?硕迪科技

SOLIDWORKS作为一款三维设计软件&#xff0c;为用户提供了多种特征造型方法&#xff0c;以下是其中几种常用的&#xff1a; 实体建模特征&#xff1a;SOLIDWORKS使用实体建模技术来创建和编辑三维几何体。通过使用基本几何体&#xff08;如立方体、圆柱体、圆锥体等&#xff09…

【广州华锐互动】楼宇智能化VR虚拟教学系统

在如今的技术时代&#xff0c;教育行业正在逐步引入各种创新方法以提升教学质量。VR公司广州华锐互动开发的楼宇智能化VR虚拟教学系统就是其中的一种&#xff0c;它利用虚拟现实(VR)技术&#xff0c;为学生提供一种全新的、沉浸式的学习体验。 楼宇智能化VR虚拟教学系统涵盖综合…

“我为家乡代言”第三届iEnglish英语风采秀即将启动

近期,第二届iEnglish英语风采秀活动圆满落幕,超2万名来自全国各地的青少年儿童通过此平台展示了他们的英语才华和自信。如今,第三届iEnglish英语风采秀即将于12月底正式启动。 据悉,iEnglish英语风采秀旨在为全国青少年儿童提供一个专属于自己的英语展示舞台,通过英文演讲的模…

19.5 Boost Asio 传输结构体

同步模式下的结构体传输与原生套接字实现方式完全一致&#xff0c;读者需要注意的是在接收参数是应该使用socket.read_some函数读取&#xff0c;发送参数则使用socket.write_some函数实现&#xff0c;对于套接字的解析同样使用强制指针转换的方法。 服务端代码如下所示 #incl…

班级新闻管理系统asp.net+sqlserver

班级新闻管理系统 附加功能 新闻图片&#xff0c;点击次数访问自增&#xff0c;每个人都只能增删改查自己发布的新闻&#xff0c;并可以看到所有人发布的新闻 运行前附加数据库.mdf&#xff08;或sql生成数据库&#xff09; 主要技术&#xff1a; 基于asp.net架构和sql serve…

在IDEA中使用maven项目总结

一 什么是mavenMaven本身也是Java写的,他是一款服务于Java平台的自动化构建工具Maven是一个项目管理工具,旨在简化软件项目的构建、依赖管理和项目信息管理。它使用基于项目对象模型(Project Object Model,POM)的概念来管理项目的构建和依赖,并提供了一套规范的项目结构…

flutter 使用 hive 遇到的错误

1. ] Unhandled Exception: RangeError: Not enough bytes available. 根据日志定位到 下图的地方 解决&#xff1a;因为之前存在保存到本地的信息&#xff0c;但是你修改了 数据类里面的东西&#xff08;比如添加变量啥的&#xff09;&#xff0c;清空app缓存或者卸载重新构…

使用微信小程序控制蓝牙小车(微信小程序端)

目录 使用接口界面效果界面设计界面逻辑设计 使用接口 微信小程序官方开发文档 接口说明wx.openBluetoothAdapter初始化蓝牙模块wx.closeBluetoothAdapter关闭蓝牙模块(调用该方法将断开所有已建立的连接并释放系统资源)wx.startBluetoothDevicesDiscovery开始搜寻附近的蓝牙…

处理uniapp打包后有广告的问题

1、登录平台&#xff08;开发者中心&#xff09; 2、 3、 4、 5、