窗口函数(开窗函数)及OVER子句(2):窗口与分组的区别

窗口函数(开窗函数)及OVER子句(2):窗口与分组的区别

若觉得本文写得还可以,请多多关注本人所作书籍《C++语法详解》电子工业出版社出版,作者 黄勇,网盘地址:
https://pan.baidu.com/s/1dIxLMN5b91zpJN2sZv1MNg

本文为原创文章,转载请注明出处,或注明转载自“黄邦勇帅(原名:黄勇)

三、窗口函数的特点及与分组、子查询的区别
1、窗口函数和聚合函数都是作用于行集的,因此,使用窗口聚合函数能实现的方案,通常可以使用分组查询或子查询替代,但是窗口聚合函数通常优于替代方案,下面对其进行讲解。
2、不同的窗口函数可以使用不同的窗口进行计算,而分组查询的分组是在查询中定义的,所有计算都使用相同的集合进行。比如,

select a,sum( c), avg ( c) from T4 group by a; 

以上分组语句中sum 和avg的计算都是在由列a产生的分组上进行计算的

select a,sum( c) over (partition by a),avg( c) over(partition by b) from T4

以上语句中的窗口聚合函数sum是在由列a定义的窗口上进行计算的,而avg是在由列b定义的窗口上进行计算的。
3、窗口函数不会丢失详细信息
窗口函数是根据窗口进行计算的,并且会为查询结果集的每一行返回一个标量结果值,分组查询一个组只返回一个值,因此分组查询会失去细节信息,而窗口函数则不会。比如
select a, sum( c) from T group by a
以上语句对c列求和,由于是仅使用了列a进行分组,因此无法查看到c列各行值的详细信息,若使用窗口函数,则可以查看到c列及其他列各行值的详细信息,比如

select a,c,sum( c) over(partition by a) from T4

当实际应用中需要计算聚合信息和详细信息时,窗口函数比分组查询能更好,详见后文。
4、对使用了相同窗口的窗口函数,只会对窗口内的数据访问一次,即只会对源表进行一次扫描,从而提高性能。从此优点可见,窗口函数的性能明显高于使用子查询替代窗口函数的方案,详见后文的示例
5、窗口是在对表进行了表操作符(如联接)、筛选(比如WHERE子句)、分组之后的结果集基础之上定义的,这样就不用重复设定对外部查询的限定,而子查询是从头开始的查询,需要重复设定对外部查询的限定,因此窗口函数的代码会明显短于使用子查询替代窗口函数的方案,并且可提高性能,比如

select a, sum(c) over (partition by a)from T4 where a=5	--窗口函数
select a,
(select sum(c)from T4 	--使用子查询替代窗口函数
where a=5   			--外部查询的WHERE子句需要重新限定,增加了代码长度
group by a ) from T4 where a=5 

6、若需要对窗口函数的计算结果进行筛选,则可使用公用表表达式CTE来解决,比如

WITH S AS(select a, sum(c) over(partition by a) AS x from T4)
select * from S where x=3

7、注意SELECT语句的同时性概念,比如SELECT a as x, x+b FROM T4;是错误的,因为a as x与x+b是同时执行的,因此,在执行x+b时,列a的别名x还未创建成功。同时性概念意味着,在同一时刻,所有的窗口是同时存在的。
示例:表T4具有(dat,val)两列,其中dat表示日期,val表示当月的销量,试计算当月销量占年总销量的百分比,当月销量占年平均销量的百分比
解:当月销量为详细信息,年总销量和年平均销量是聚合信息,若使用分组查询进行处理,将比较复杂,若使用窗口函数,则比较容易实现。下面为3种实现方法
方法1:使用分组查询

WITH TT as 								--使用公用表表达式(CTE)存储聚合信息
(	select year(dat)AS y, 
sum(val)AS yval,avg(val)as v 		--计算年总销量和年平均销量
from T7 group by year(dat))
select dat,val,							--显示详细信息
			100.0*val/yval,100.0*val/v 			--需要求解的数据
from T7 join TT on year(T7.dat)=TT.y		--还需要把CTE与表T7进行联接

方法2:使用窗口函数

select dat,val										--显示的详细信息
		,100.0*val/sum(val) over(partition by year(dat))		--当前月销量与年总销量之比
	 , 100.0*val/avg(val) over(partition by year(dat)) 	--当前月销量与年平均销量之比
from T7;

方法3:使用子查询

select dat,val
		 ,100.0*val/(select sum(val) from T7 group by year(dat))
		,100.0*val/(select avg(val) from T7 group by year(dat)) 

由以上示例可见
1)、分组查询需要引入CTE和联接,当需要计算的量越来越多时,就需要更多的CTE和联接,因此分组查询会变得越来越复杂。由此可见,窗口函数可很好的解决这个问题。
2)、子查询有以下问题,第一,代码太长,第二,对于每个子查询,都需要单独扫描一次源表,因此,子查询越多,扫描源表的次数就越多,从而性能就越低,另外,子查询还需要对外部查询的限定(比如WHERE子句)进行重复限定。从以上对窗口函数的讲解可知,窗口函数可很好的解决子查询的这些问题

作者:黄邦勇帅(原名:黄勇)

窗口函数(开窗函数)及OVER子句(2):窗口与分组的区别_第1张图片

主要参考文献:
1、C++语法详解 黄勇 编著 电子工业出版社 2017年7月
2、SQL Server 2012 T-SQL基础教程 [美] Itzik Ben-Gan著 张洪举 李联国 张昊天 译 人民邮电出版社 2013年12月
3、T-SQL性能调优秘笈------基于SQL Server 2012窗口函数 [美] Itzik Ben-Gan著 林德玲 方鑫译 人民邮电出版社 2014年8月
4、SQL Server 2005技术内幕:T-SQL查询 [美] Itzik Ben-Gan , Lubor Kollar , Dejan Sarka著 赵立东 唐灿 刘波 译 赵立东 审校 电子工业出版社 出版日期不详
4、SQL Server 2005技术内幕:T-SQL程序设计 [美] Itzik Ben-Gan , Dejan Sarka , Roger Wolter著 赵立东 译 电子工业出版社 出版日期不详
5、Microsoft SQL Server 2008技术内幕:T-SQL语言基础 [美] Itzik Ben-Gan著 成保栋 张昱 译 电子工业出版社 2009年10月
6、Microsoft SQL Server 2008技术内幕:T-SQL查询 [美] Itzik Ben-Gan , Lubor Kollar , Dejan Sarka , Steve Kass著 成保栋 李保强 译 电子工业出版社 2010年9月

你可能感兴趣的:(SQL,Server)