暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

SQL笔记| 窗口函数

chkl 2025-05-30
28

必知必会的窗口函数

如果熟练掌握了窗口函数,从某种意义上来说,我们就可以像使用面向过程语言那样操作数据。窗口函数能够大幅扩展SQL编程的可能性,是一个非常重要的工具。
窗口函数的应用方法有很多,特别是在进行行间比较时必须依赖相关子查询的情况下,通过使用窗口函数,我们可以去掉相关的子查询,让SQL语句变得更加优雅。

1、什么是窗口

窗口函数出现于20世纪90年代到21世纪初,当时它也被称为“OLAP"(联机分析处理,online analytical procession)函数。
匿名窗口:

SELECT products_id,products_name,sale_price,
	AVG(sale_price) OVER (ORDER BY products_id ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg 
FROM Products;

有名称的窗口:

SELECT products_id,products_name,sale_price,
	AVG(sale_price) OVER W  AS moving_avg 
FROM Products 
WINDOW W AS (ORDER BY products_id ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) ;

这里显式定义了窗口,并对其应用了AVG函数。这里所说的窗口,就是针对通过FROM子句选择的记录集,使用ORDER BY排序和使用ROWS BETWEEN定义帧之后所形成的数据集。
有名称的窗口可以被重复使用:

SELECT products_id,products_name,sale_price,
	AVG(sale_price) OVER W  AS moving_avg, 
	SUM(sale_price) OVER W  AS moving_sum, 
	COUNT(sale_price) OVER W  AS moving_count, 
	MAX(sale_price) OVER W  AS moving_max, 
FROM Products 
WINDOW W AS (ORDER BY products_id ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) ;

注意:有名称的窗口函数可以用在postgresql和mysql中,但在oracle中使用就会发生错误。

2、一张图看懂窗口函数

窗口函数让人难以理解的原因之一是1个窗口函数中包括多个操作。窗口函数实际上包含下面3个功能。
1)使用PARTITION BY子句分割记录集合。
2)使用ORDER BY子句对记录排序。
3)使用帧子句定义一当前记录为中心的子集。
这个“当前记录”源自“游标”(cursor)的引入——关系数据库一直使用游标向面向过程语言传递数据。
之所以需要游标,是因为关系数据库的表中的记录是无序的,操作的基本单位是记录的集合,也就是一次一集合(set at a time)的操作方式,而面向过程语言的记录是有序的,操作的基本单位是一行记录,也就是一次一记录(record at a time)的操作方式,我们需要用游标来填补二者之间的差异。
在面向过程语言中,根据键对记录集合进行排序,通过for语句或while语句循环记录集合,一行一行地移动当前记录进行处理,这种操作方法至今没有变过。即使在引入地址隐藏和面向对象后,也没有发生改变。在这一点上,窗口函数可以说是将面向过程语言的思想引入到了SQL中。

3、使用帧子句将其他行移至当前行

求过去最临近的值

--过去最临近的日期,也就是上一行
SELECT sample_data  AS cur_date,load_val AS cur_load,
	MIN(sample_date) OVER (ORDER BY sample_date ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING ) AS latest_date ,
	MIN(load_val ) OVER (ORDER BY sample_date ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING ) AS latest_load 	
FROM LoadSample;
--向后移动一行
SELECT sample_data  AS cur_date,load_val AS cur_load,
	MIN(sample_date) OVER W AS latest_date ,
	MIN(load_val ) OVER W AS latest_load 	
FROM LoadSample
WINDOW W AS (ORDER BY sample_date ASC ROWS BETWEEN 1 FOLLOWING AND 1 FOLLOWING );
--可以设置“一天前”或“两天前”这样基于列值(而不是行)的帧
SELECT sample_data  AS cur_date,load_val AS cur_load,
	MIN(sample_date) OVER W AS latest_date ,
	MIN(load_val ) OVER W AS latest_load 	
FROM LoadSample
WINDOW W AS (ORDER BY sample_date ASC RANGE BETWEEN interval '1' PRECEDING AND  interval '1' PRECEDING );

下面是帧子句中可以使用的选项,大家可以参考。
ROWS:按行设置移动单位。
RANGE:按列值设置移动单位。使用ORDER BY子句来指定基准列。
n PRECEDING:仅向前(行号较小的方向)移动n行。n为正整数
n FOLLWING:仅向后(行号较大的方向)移动n行。n为正整数
UNBOUNDED PRECEDING:一直移动到最前面
UNBOUNDED FOLLOWING:一直移动到最后面
CURRENT ROW:当前行

4、窗口函数的内部动作

查看SQL语句内部动作的手段通常是查看“执行计划”(execution plan)。所谓执行计划,其实就是一份由数据库提供的计划书,以帮着我们确定DBNS在执行SQL语句时,以什么样的访问路径获取数据、执行什么样的计算是最高效的。PostgreSql的执行计划中出现了SORT关键字,MySQL的执行计划中出现了Using filesort关键字,它们都表示排序。
窗口函数的本质是排序。

5、习题

SELECT server,sample_date,SUM(load_val) OVER() AS sum_load 
 FROM ServerLoadSample ;

sum_load列的所有行的值都是同一个值,把整张表作为一个大组进行了处理求和。

SELECT server,sample_date,SUM(load_val) OVER(PARTITION BY server) AS sum_load 
 FROM ServerLoadSample ;

sum_load列的按照server列分组,并对各组执行SUM函数的计算。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论