MySQL8窗口函数

这篇具有很好参考价值的文章主要介绍了MySQL8窗口函数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、窗口函数简介

在日常开发工作中,经常会遇到下面这些需求

  • 去医院看病,怎样知道上次就医距现在的时间?
  • 环比如何计算?
  • 怎么样得到各部门工资排名前N名员工列表?
  • 查找各部门每人工资占部门总工资的百分比?

如果用传统SQL来解决这些问题,理论上是可以的,但逻辑会相当复杂。这类需求都有一个共同的特点,为了的到结果,都需要在某个结果集内做一些特定的函数操作。为了很方便的解决这一类问题,MySQL8.0中引入了窗口函数。

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

窗口函数和聚合函数有些类似,两者最大的区别是聚合函数是多行聚合为一行,窗口函数则是多行聚合为相同的行数,每行会多一个聚合后的新列(在窗口函数中可以使用聚合函数)。窗口函数在其他数据库中(比如ORACLE数据库)也叫做分析函数,但功能大多相似。

举例:先创建表结构并导入测试数据

CREATE TABLE sales(
   id INT PRIMARY KEY AUTO_INCREMENT,
	city VARCHAR(15),
	county VARCHAR(15),
	sales_value DECIMAL
);
INSERT INTO sales ( city, county, sales_value )
VALUES
	( '北京', '海淀', 10.00 ),
	( '北京', '朝阳', 20.00 ),
	( '上海', '黄埔', 30.00 ),
	( '上海', '长宁', 10.00 );

现在计算这个网站在每个城市的销售总额、在全国的销售总额、每个区的销售额占所在城市销售

额中的比率,以及占总销售额中的比率。若使用窗口函数可以很简单的实现:

SELECT
	city,
	county,
	sales_value AS 区销售金额,
	SUM( sales_value ) OVER ( PARTITION BY city ) AS 市销售额,
	sales_value / sum( sales_value ) OVER ( PARTITION BY city ) AS 市比率,
	SUM( sales_value ) OVER () AS 总销售额,
	sales_value / SUM( sales_value ) OVER () AS 总比率 
FROM
	sales

结果如下:

MySQL8窗口函数

显然在这种需要用到分组统计的结果对每一条记录进行计算的场景下,使用窗口函可以很简单的实现

二、窗口函数的语法结构

窗口函数总体上可以分为序号函数、分布函数、前后函数、首尾函数和其他函数,如下表:

MySQL8窗口函数

其基本语法结构如何

函数 OVER([PARTITION BY 字段名 ORDER BY 字段名 ASC|DESC])
函数 OVER window_name... WINDOW window_name AS ([PARTITION BY 字段名 ORDER BY 字段名 ASC|DESC])
  • OVER 关键字指定函数窗口的范围。

    ​ 如果省略后面括号中的内容,则窗口会包含满足WHERE条件的所有记录,窗口函数会基于所

    有满足WHERE条件的记录进行计算。

    ​ 如果OVER关键字后面的括号不为空,则可以使用如下语法设置窗口。

  • window_name:为窗口设置一个别名,用来标识窗口。

  • PARTITION BY子句:指定窗口函数按照哪些字段进行分组。分组后,窗口函数可以在每个分组中分

    别执行。

  • ORDER BY子句:指定窗口函数按照哪些字段进行排序。执行排序操作使窗口函数按照排序后的数据

    记录的顺序进行编号。

  • FRAME子句:为分区中的某个子集定义规则,可以用来作为滑动窗口使用。

三、窗口函数的使用

添加测试数据

CREATE TABLE order_tab( 
	order_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '订单号', 
	user_no INT COMMENT '用户ID', 
	amount int COMMENT '订单金额', 
  create_time DATETIME  COMMENT' 订单创建日期'
);

INSERT INTO order_tab ( order_id, user_no, amount, create_time)
VALUES
	(1,001,100, '2018-01-01 00:00:00'),
	(2,001,300, '2018-01-02 00:00:00'),
	(3,001,500, '2017-01-01 00:00:00'),
	(4,001,800, '2017-01-07 00:00:00'),
	(5,001,1000, '2018-01-04 00:00:00'),
	(6,002,500, '2018-01-04 00:00:00'),
	(7,002,300, '2018-01-04 00:00:00'),
	(8,002,1000, '2017-01-04 00:00:00'),
	(9,002,8000, '2019-01-04 00:00:00'),
	(10,002,2000, '2019-01-04 00:00:00')
3.1、序号函数
  1. ROW_NUMBER()函数:ROW_NUMBER()函数能够对分区中的每一行记录增加一个序号,

    如需查询每个用户最新的一笔订单便可以使用ROW_NUMBER()函数按照用户进行分组并按照订单日期倒序,最后找出每组中序号为1的记录。

    SELECT * FROM
    	( SELECT *, ROW_NUMBER() OVER ( PARTITION BY user_no ORDER BY create_time DESC ) AS 	row_num FROM order_tab ) t 
    WHERE
    	row_num = 1
    	
    # 或者使用别名
    SELECT * FROM
    	( SELECT *, ROW_NUMBER() OVER w AS row_num FROM order_tab WINDOW w AS ( PARTITION BY user_no ORDER BY create_time DESC ) ) t 
    WHERE
    	row_num = 1
    

MySQL8窗口函数

row_number()后面的over是关键字,用来指定函数执行的窗口范围,如果后面的括号中什么都不写,则意味着窗口包含所有行,窗口函数在所有行上进行计算。

如要根据根据每个订单动态计算包括本订单和按时间顺序前后两个订单的平均订单金额,则可以使用上边提到的frame子句来创建滑动窗口

SELECT * FROM
	( 
		SELECT 
		  *,
			AVG(amount) OVER  w AS avg_num
		FROM order_tab	
	   WINDOW w AS ( PARTITION BY user_no ORDER BY create_time DESC ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
	) t 

MySQL8窗口函数

从结果可以看出,order_id为5的订单属于边界值,没有前一行,因此平均订单金额为(1000+300)/2;order_id等于2的订单前后都有订单,所以平均订单金额为(1000+300+100)/3 = 466.6667,以此类推就可以得到一个基于滑动窗口的动态平均订单值。

对于滑动窗口的范围指定,有如下两种方式。

  • 基于行:通常使用 BETWEEN frame_start AND frame_end 语法来表示行范围,frame_start和frame_end可以支持如下关键字,用以确定不同的动态行记录。

    CURRENT ROW # 边界是当前行,一般和其它范围关键字一起使用
    UNBOUNDED PRECEDING # 边界是分区中第一行
    UNBOUNDED FOLLOWING # 边界是分区中最后一行
    expr PRECEDING # 边界是当前行减去expr的值
    expr FOLLOWING # 边界是当前行加上expr的值
    

    比如下边都是合法的范围

    ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING # 窗口范围是当前行,当前行前一行和当前行后一行共三行记录
    ROWS UNBOUNDED FOLLOWING # 边界是当前行到分区中的最后一行
    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING # 窗口范围是当前分区中的所有行,等同于不写
    
  • 基于范围:和基于行类似,但有些范围不是直接可以用行数来表示的,比如希望窗口函数是一周前的订单开始,截止到当前行,则无法使用rows来表示,此时就可以使用范围来表示窗口:INTERVAL 7 DAY PRECEDING。

  1. RANK()函数/DENSE_RANK()函数:

    RANK()和DENSE_RANK()这两个函数与ROW_NUMBER()非常类似。只是在出现重复值时处理逻辑有所不同,假设需要查询不同用户的订单,按照订单时间进行排序,显示相应的排名序号,SQL中使用ROW_NUMBER()、RANK()、DENSE_RANK()分别显示序号

MySQL8窗口函数

如图,ROW_NUMBER在create_time都是2019-01-04 00:00:00的两条记录随机排序但序号按照1、2递增,其后面的序号不会产生序号空隙,RANK()/DENSE_RANK()则把2019-01-04 00:00:00的两条记录序号都设置为1,但后边紧接着的序号则分别设置为3(RANK)和2(DENSE_RANK),很明显RANK()和DENSE_RANK()会产生相同的序号但是RANK()会产生序号间隙而DENSE_RANK()并不会产生序号间隙。

3.2、分布函数
  1. PERCENT_RANK()函数/CUME_DIST()函数

    PERCENT_RANK()函数和CUME_DIST()函数都是计算数据分布的函数,PERCENT_RANK()和之前的RANK()函数相关,每行按照以下公式进行计算

    (rank -1 ) / (rows -1)
    

    其中rank为RANK()函数产生的序号,rows为当前窗口的记录总行数

    SELECT
    	* 
    FROM
    	( 
    	SELECT *, 
    			ROW_NUMBER() OVER ( PARTITION BY user_no ORDER BY create_time DESC ) AS row_num,
    			PERCENT_RANK() OVER ( PARTITION BY user_no ORDER BY create_time DESC ) percent		
    	FROM order_tab 
    	) t 
    

    结果如下:

MySQL8窗口函数

相较于PERCENT_RANK(),CUME_DIST()函数的应用场景更多。它的作用是分组内小于等于当前rank值的行数/分组内总行数。上例中,统计大于等于当前订单金额的订单数,占总订单数据的比例如下:

结果如下:

MySQL8窗口函数

3.3、前后函数
  1. LAG(expr,N)函数/LEAD(expr,N)函数

    LAG(expr,N)和LEAD(expr,N)这两个函数的功能是获取当前数据行按照某种排序规则的上N行(LAG)/下N行(LEAD)数据的某个字段。比如每个订单中希望增加一个字段,用来记录本订单距离上一个订单的时间间隔,那么就可以使用LAG函数来实现:

    	
    SELECT
    	*,
    	DATEDIFF( create_time,last_time ) 
    FROM
    	( SELECT *, LAG( create_time,1 ) OVER w AS last_time FROM order_tab WINDOW w AS ( PARTITION BY user_no ORDER BY create_time ASC ) ) t
    

    结果如下:

MySQL8窗口函数

3.4、首尾函数
  1. FIRST_VALUE(expr)函数/LAST_VALUE(expr)函数

    FIRST_VALUE(expr)函数和LAST_VALUE(expr)函数的功能分别是获得滑动窗口范围内的参数字段中第一个(FIRST_VALUE)和最后一个(LAST_VALUE)的值。下例中。每个用户在每个订单记录中希望看到截止到当前订单为止,按照日期排序的最早订单和最晚订单的订单金额。

    SELECT
    	*
    FROM
    	( SELECT 
    			*, 
    			FIRST_VALUE(amount) OVER w AS first_amount,
    			LAST_VALUE(amount) OVER w AS last_amount
    	 FROM order_tab WINDOW w AS ( PARTITION BY user_no ORDER BY create_time ASC ) ) t
    

    ​ 结果如下:

MySQL8窗口函数

3.5、其它函数
  1. NTILE(n)函数

    NFILE()函数的功能是对一个数据分区中的有序结果集进行划分,将其分为N个组,并未每个小组分配一个唯一的组编号。接下来我们对每个用户的订单分为3组,NFILE()函数记录每组组编号

    SELECT
    	* 
    FROM
    	( SELECT *, NTILE(3) OVER w AS nf FROM order_tab WINDOW w AS ( PARTITION BY user_no ORDER BY amount ASC ) ) t
    

    结果如下:
    MySQL8窗口函数

次函数爱数据分析中应用较多,比如由于数据量大,需要将数据分配到N个并行的进程中分别计算,此时就可以用NTILE(N)函数对数据进行分组,由于记录数不一定被N整除,所以每组记录数不一定完全一致。

  1. NTH_VALUE(expr,n)函数

    NTH_VALUE(expr,N)函数可以返回窗口中第N个expr的值,expr既可以是表达式,也可以是列名。我们可以查找每个用户的所有订单按照金额排序的第三个订单的金额.

    SELECT
    	* 
    FROM
    	( SELECT 
    				*,
    				NTH_VALUE(amount,2) OVER w AS nth_amount
    		FROM order_tab 
    		WINDOW w AS ( PARTITION BY user_no ORDER BY amount ASC ) 
    	) t
    

    结果如下:
    MySQL8窗口函数

    3.6、聚合函数作为窗口函数

    除了以上的各类窗口函数外,我们经常使用各种聚合函数来作为窗口函数使用

    	SELECT 
    				*,
    				sum(amount) OVER w AS sum_value,
    				avg(amount) OVER w AS avg_value,
    				max(amount) OVER w AS max_value,
    				min(amount) OVER w AS min_value,
    				count(amount) OVER w AS count_value
    		FROM order_tab 
    		WINDOW w AS ( PARTITION BY user_no ORDER BY amount ASC ) 
    

    结果如下:

    MySQL8窗口函数文章来源地址https://www.toymoban.com/news/detail-505642.html

到了这里,关于MySQL8窗口函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • MYSQL窗口函数(Rows & Range)——滑动窗口函数用法

    窗口函数语法: 窗口函数 over (partition by 用于分组的列名 order by 用于排序的列名 rows/range子句用于定义窗口大小 ) 窗口函数可以放以下两种函数: 1) 专用窗口函数,包括后面要讲到的rank, dense_rank, row_number等专用窗口函数。 2) 聚合函数,如sum. avg, count, max, min等 1)专用窗口

    2024年02月10日
    浏览(29)
  • mysql中的窗口函数

    MySQL中的窗口函数(Window Functions)是一种用于在查询结果集内执行计算的功能。窗口函数可以在查询中进行分析和聚合操作,而无需将查询结果分组。它们可以用于计算排名、行号、累积值等各种分析操作。窗口函数通常与OVER子句一起使用,用于指定窗口的范围。 常见的窗

    2024年02月12日
    浏览(74)
  • 【SQL】MySQL中的窗口函数(开窗函数)

    窗口函数是 MYSQL8.0 新增的 聚合函数: 多行变一行,常见的sum,count,max,min 窗口函数: 行数不变,常见的row_number,rank 语法格式: 窗口函数(表达式) over (partition by … order by … frame_clause) partition by是分区,类似于group by,如去掉相当于对所有数据进行计算 order by排序 frame_c

    2024年02月07日
    浏览(34)
  • MySQL窗口函数(MySQL Window Functions)

           MySQL从8.0开始支持窗口函数(Window Functions),因其经常被用在数据分析的场景,窗口函数也被称为分析函数(Analytic Functions)。 目录 一、窗口函数概念 二、基础语法 2.1 窗口的定义 2.2 命名窗口 三、专用窗口函数 3.1 row_number() 3.2 rank() 3.3 dense_rank() 3.4 percent_rank() 3.5

    2024年02月11日
    浏览(33)
  • 深入MySQL窗口函数:原理和应用

    在现代数据库管理系统中,窗口函数(Window Functions)已经成为处理复杂数据分析任务的关键工具。MySQL从8.0版本开始引入了对窗口函数的支持,这极大地增强了其在数据分析和报表生成方面的能力。本文将深入探讨MySQL窗口函数的原理、应用场景以及优化策略。 窗口函数(

    2024年01月23日
    浏览(31)
  • MySQL---控制流函数、窗口函数(序号函数、开窗聚合函数、分布函数、前后函数、头尾函数、其他函数)

    格式 解释 案例 IF(expr,v1,v2) 如果表达式 expr 成立,返回结果 v1 ;否则,返回结果 v2 。 SELECT IF(1 0,\\\' 正确 \\\',\\\' 错误 \\\')    - 正确 IFNULL(v1,v2) 如果 v1 的值不为 NULL ,则返回 v1 ,否则返回 v2 。 SELECT IFNULL(null,\\\'Hello Word\\\') -Hello Word ISNULL(expression) 判断表达式是否为 NULL SELECT ISNULL(NUL

    2024年02月04日
    浏览(31)
  • 【MySQL】MySQL版本8+ 窗口函数 Lead 的两种使用

    1709. 访问日期之间最大的空档期 表:UserVisits Column Name Type user_id int visit_date date 该表没有主键,它可能有重复的行 该表包含用户访问某特定零售商的日期日志。 假设今天的日期是 ‘2021-1-1’ 。 编写解决方案,对于每个 user_id ,求出每次访问及其下一个访问(若该次访问是最

    2024年01月22日
    浏览(33)
  • mysql 8.0 窗口函数 之 序号函数 与 sql server 序号函数 一样

    sql server 序号函数 序号函数 ROW_NUMBER() 顺序排序 RANK() 并列排序,会跳过重复的序号,比如序号为1,1,3 DENSE_RANK() 并列排序,不会跳过重复的序号,比如 序号为 1,1,2 语法结构 窗口函数的优点 使用窗口函数,只用了一步就完成了查询,而且,由于没有用到临时表,执行的

    2024年02月11日
    浏览(28)
  • mysql 8.0 窗口函数 之 分布函数 与 sql server (2017以后支持) 分布函数 一样

    mysql 分布函数 percent_rank() :等级值 百分比 cume_dist() :累积分布值 percent_rank() 计算方式 (rank-1)/(rows-1), 其中 rank 的值为使用RANK()函数产生的序号,rows 的值为当前窗口的总记录数。

    2024年02月11日
    浏览(33)
  • windows下全免费手动搭建php8+mysql8开发环境及可视化工具安装

    最近PHP项目少了,一直在研究UE5和Golang,但是考虑到政府、国企未来几年国产化的要求,可能又要重拾PHP。于是近日把用了N年的框架重新更新至适合PHP8.2以上的版本,同时也乘着新装机,再次搭建php和mysql开发环境。本文留个记录,以后方便操作。 选择最新版下载 https://ww

    2024年01月20日
    浏览(69)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包