SQL Server 中的窗口函数(2012 新函数)
简介
SQL Server 2012之后对窗口函数进行了极大的加强,但对于很多开发人员来说,对窗口函数却不甚了解,导致了这样强大的功能被浪费,因此本篇文章主要谈一谈SQL Server中窗口函数的概念。
什么是窗口函数
窗口函数,也可以被称为OLAP函数或分析函数。理解窗口函数可以从理解聚合函数开始,我们知道聚合函数的概念,就是将某列多行中的值按照聚合规则合并为一行,比如说Sum、AVG等等,简单的概念如图1所示。
图1.聚合函数
因此,通常来说,聚合后的行数都要小于聚合前的行数。而对于窗口函数来说,输入结果等于输出结果,举一个简单的例子,如果你计算产品类型A和产品类型B,A产品分5小类,B产品分2小类,应用了窗口函数的结果后可以还是7行,对窗口函数应用了Count后,附加在每一行上,比如说“A产品,A小类1,5“,而B小类则变为”B产品,B小类1,2”最后一列就是应用了窗口函数的结果。
现在我们对窗口函数有了初步的概览,文章后我会提供一些具体的例子来让对窗口函数的概念更加深刻,窗口函数除了上面提到的输入行等于输出行之外,还有如下特性和好处:
- 类似Group By的聚合
- 非顺序的访问数据
- 可以对于窗口函数使用分析函数、聚合函数和排名函数
- 简化了SQL代码(消除Join)
- 消除中间表
窗口函数是整个SQL语句最后被执行的部分,这意味着窗口函数是在SQL查询的结果集上进行的,因此不会受到Group By, Having,Where子句的影响。
窗口函数的典型范例是我们在SQL Server 2005之后用到的排序函数,比如代码清单1所示。
Row_Number() OVER (partition by xx ORDER BY xxx desc) RowNumber
代码清单1.可用于分页的排序函数
因此,我们可以把窗口函数的语法抽象出来,如代码清单2所示。
函数() Over (PARTITION By 列1,列2,Order By 列3,窗口子句) AS 列别名
代码清单2.窗口函数的语法
一个简单的例子
下面我们来看一个简单的例子,假如说我们希望将AdventureWorks示例数据库中的Employee表按照性别进行聚合,比如说我希望得到的结果是:“登录名,性别,该性别所有员工的总数”,如果我们使用传统的写法,那一定会涉及到子查询,如代码清单3所示。
SELECT [LoginID],gender,
(SELECT COUNT(*) FROM [AdventureWorks2012].[HumanResources].[Employee] a WHERE a.Gender=b.Gender) AS GenderTotal
FROM [AdventureWorks2012].[HumanResources].[Employee] b
代码清单3.传统的写法
如果我们使用了窗口函数,代码瞬间就变得简洁,不再需要子查询或Join,如图2所示。
图2.使用窗口函数
除此之外,窗口函数相比传统写法而言,还会有更好的性能,我们可以通过比较执行计划得出如图3所示。
图3.通过比较执行计划,看出窗口函数拥有更好的性能
假如我们考虑更复杂的例子,在Over子句加上了Order By,来完成一个平均数累加,如果不使用窗口函数,那一定是游标,循环等麻烦的方式,如果使用了窗口函数,则一切就变得非常轻松,如图4所示。
图4.窗口函数
Partition By
代码清单2展示了窗口函数的语法,其中Over子句之后第一个提到的就是Partition By。Partition By子句也可以称为查询分区子句,非常类似于Group By,都是将数据按照边界值分组,而Over之前的函数在每一个分组之内进行,如果超出了分组,则函数会重新计算,比如图2中的例子,我们将数据分为男性和女性两部分,前面的Count()函数针对这两组分别计算值(男性206,女性84)。
针对Partition By可以应用的函数不仅仅是我们所熟知的聚合函数,以及一些其他的函数,比如说Row_Number()。
Order By
Order By子句是另一类子句,会让输入的数据强制排序(文章前面提到过,窗口函数是SQL语句最后执行的函数,因此可以把SQL结果集想象成输入数据)。Order By子句对于诸如Row_Number(),Lead(),LAG()等函数是必须的,因为如果数据无序,这些函数的结果就没有任何意义。因此如果有了Order By子句,则Count(),Min()等计算出来的结果就没有任何意义。
下面我们看一个很有代表性的ROW_NUMBER()函数,该函数通常被用于分页,该函数从1开始不断递增,可以和Partition By一起使用,当穿越分区边界时,Row_Number重置为1,一个简单的例子如图5所示,我们根据请假小时数对员工进行排序。
图5.Row_Number函数示例
另一个比较有趣的分析函数是LEAD()和LAG(),这两个分析函数经过Order By子句排序后,可以在当前行访问上N行(LAG)或下N行(LEAD)的数据,下面是一个例子,如图6所示。
图6.访问上一行的LAG函数
另一个分析函数是RANK函数,与Row_Number不同的是,Rank函数中如果出现了相同的值,不会像Row_Number那样叠加计数,而是同样的值计数一样,比如说 1 1 3 4 5 5 7,而不是Row_Number的1 2 3 4 5 6 7。这里就不细说了。另外如果希望并列排名的不影响下一个排名,则考虑使用Dense_Rank函数。有关其他的诸如First_value和Last_Value之类的函数可以参看:http://technet.microsoft.com/zh-cn/library/hh213234.aspx。
窗口子句
前面窗口的函数的作用范围是整个表,或是整个Partition by后面的分区。但是使用了窗口子句我们可以控制输入到窗口函数的数据集(前面说过,窗口函数是整个语句中最后执行的)的范围。下面我们从一个例子开始看,假如我希望找出公司每一个层级休病假最长的人,我们可以执行图7中的语句。
图7.找出每个层级休假最多的人
但是如果我们希望把输入数据集的粒度由Partition变为更细的话,我们可以使用窗口子句,让窗口函数仅仅根据当前行的前N行和后N行计算结果,那我们可以使用窗口子句,如图8所示,图8中,我们排序后,仅仅根据当前行的前一行和后一行以及当前行来计算这3个人当中请病假最长时间的人。
图8.在三行之内找到休假时间最长的人
我们也可以使用Range来指定Partition内的范围,比如说我们希望从当前行和之前行中找到第一行,则使用如图9所示的用法。
图9.
补充
NTILE 函数
NTILE 函数可以按照指定的排序规则,对数据按照指定的组数(M个对象,按照某种排序分N个组)进行分组,可以展现出某一条数据被分配在哪个组中.(如果记录出现基数时则按照前多后少的规则分)
图10.
根据以上可以看出,当记录数为4分为2组时,每组两条记录顺序排名。当记录数为5分为2组时,则按照前多后少的规则(前3后2)顺序排名。
CUME_DIST函数
计算某个值在 SQL Server 2012 中的一组值内的累积分布。也即,CUME_DIST 计算某指定值在一组值中的相对位置。对于行 r,假定采用升序,r 的 CUME_DIST 是值低于或等于 r 的值的行数除以在分区或查询结果集中求出的行数。
图11.
根据以上可以看出CUME_DIST的计算规则,如有相同记录则按较大的位置取值,例如Num等于55的记录有两条,取离Num等于100这条最近的记录,则为3/5等于0.6。
PERCENT_RANK函数
计算 SQL Server 2012 中一组行内某行的相对排名。使用 PERCENT_RANK 计算一个值在查询结果集或分区中的相对位置。

图12.
PERCENTILE_CONT和PERCENTILE_DISC函数
PERCENTILE_CONT和PERCENTILE_DISC都是为了计算百分位的数值,比如计算在某个百分位时某个栏位的数值是多少。他们的区别就是前者是连续型,后者是离散型。CONT代表continuous,DISC代表discrete。PERCENTILE_CONT是连续型意味它考虑的是区间,所以值是绝对的中间值。而PERCENTILE_DISC是离散型,所以它更多考虑向上或者向下取舍,而不会考虑区间。
图13.
根据以上可以看出PERCENTILE_CONT和PERCENTILE_DISC计算规则:
PERCENTILE_CONT(0.5)计算
0.3333333333333=1/3=55(Num)
0.6666666666666=2/3=56(Num)
偏差值:x=>(56-55) / (2/3-1/3)=3
离0.5最近的最小基值 Num=55,per_rnk=1/3;则
y=55+(0.5-1/3)*3=55.5
或者
离0.5最近的最大基值 Num=56,per_rnk=2/3;则
y=56-(2/3-0.5)*3=55.5
PERCENTILE_DISC(0.5)计算
PERCENTILE_DISC(0.5)根据 (0.5-1/3)*3或者 (2/3-0.5)*3 向上/向下 (小于等于5向下) 取到两边值(55或者56),由(0.5-1/3)*3=0.5 则取55.
小结
本文从窗口函数组成的三部分简单介绍了窗口函数的概念,并给出了一些例子。更多可以在窗口上使用的函数,可以参照MSDN(http://technet.microsoft.com/zh-cn/library/ms189461.aspx)。在使用这些函数的时候,还要注意版本要求,很多函数是只有在SQL Server 2012中才被支持的。
SQL Server 中的窗口函数(2012 新函数)的更多相关文章
- SQL Server 中截取字符串常用的函数
SQL Server 中截取字符串常用的函数: 1.LEFT ( character_expression , integer_expression ) 函数说明:LEFT ( '源字符串' , '要 ...
- SQL Server中解决死锁的新方法介绍
SQL Server中解决死锁的新方法介绍 数据库操作的死锁是不可避免的,本文并不打算讨论死锁如何产生,重点在于解决死锁,通过SQL Server 2005, 现在似乎有了一种新的解决办法. 将下面的 ...
- SQL Server中的LEFT、RIGHT函数
SQL Server中的LEFT.RIGHT函数. LEFT:返回字符串中从左边开始指定个数字符. LEFT(character_expression,integer_expression); RIG ...
- SQL Server中的窗口函数
简介 SQL Server 2012之后对窗口函数进行了极大的加强,但对于很多开发人员来说,对窗口函数却不甚了解,导致了这样强大的功能被浪费,因此本篇文章主要谈一谈SQL Server中窗口函 ...
- 在SQL SERVER中实现RSA加解密函数(第二版)
/*************************************************** 作者:herowang(让你望见影子的墙) 日期:2010.1.5 注: 转载请保留此信息 更 ...
- 在SQL SERVER中实现RSA加解密函数(第一版)
/*************************************************** 作者:herowang(让你望见影子的墙) 日期:2010.1.1 注: 转载请保留此信息 ...
- 实现SQL Server中的切割字符串SplitString函数,返回Table
有时我们要用到批量操作时都会对字符串进行拆分,可是SQL Server中却没有自带Split函数,所以要自己来实现了. -- ===================================== ...
- sql server中截取字符串的常用函数
我们如果要在sql server中,使用截取字符串的方法要怎样使用呢? sql server提供了3个常用截取字符串方法,LEFT().RIGHT().SUBSTRING() /****** Sql ...
- 实现SQL Server中的切割字符串SplitString函数
有时我们要用到批量操作时都会对字符串进行拆分,可是SQL Server中却没有自带Split函数,所以要自己来实现了.没什么好说的,需要的朋友直接拿去用吧 SET ANSI_NULLS ON GO S ...
随机推荐
- java 8 学习三(Stream API)
集合讲的是数据,流讲的是计算. 流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,如filter. map. reduce. find. match. sort等. 流操作可以顺 ...
- linux 下安装git的步骤方法
①.获取github最新的Git安装包下载链接,进入Linux服务器,执行下载,命令为: wget https://github.com/git/git/archive/v2.17.0.tar.gz ...
- 如果设置Redis客户端的超时时长?
客户端的超时时长分连接超时和读写超时,如果是基于hiredis的实现,则读写超时是合在一起的,同一参数控制. 在hiredis中,读写超时调用函数redisSetTimeout设置,可以看到没有区分读 ...
- Maven配置文件setting.xml详解
注:本文来源于:大话JAVA的那些事 <Maven配置文件setting.xml详解> <?xml version="1.0" encoding="UT ...
- Problem 3 基站建设 (station.cpp)———2019.10.6
在此郑重的感激wxyww大佬 wxyww tql [题目描述]小 Z 的爸爸是一位通信工程师,他所在的通信公司最近接到了一个新的通信工程建设任务,他们需要在 C 城建设一批新的基站.C 城的城市规划做 ...
- A*G`C002
AGC002 A Range Product 不会,弃疗了/kk https://agc002.contest.atcoder.jp/submissions/7908938 B Box and Bal ...
- Java代码题目:计算奖金和完全平方数
1.计算奖金 题目:企业发放的奖金根据利润提成.利润(I)低于或等于10万元时,奖金可提10%:利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可提成7.5%:2 ...
- Harbor基础
harbor: Harbor是构建企业级私有docker镜像的仓库的开源解决方案,它是Docker Registry的更高级封装,它除了提供友好的Web UI界面,角色和用户权限管理,用户操作审计等功 ...
- 【13NOIP提高组】转圈游戏(信息学奥赛一本通 1875)(洛谷 1965)
题目描述 nn 个小描述 n 个小伙伴(编号从 0 到 n-1)围坐一圈玩游戏.按照顺时针方向给 n 个位置编号,从0 到 n-1.最初,第 0 号小伙伴在第 0 号位置,第 1 号小伙伴在第 1 号 ...
- fiddler实现B/S端、APP抓包分析遇到的各种疑问
阅读本文前您需要先下载fiddler并成功安装,并且要有一丢丢测试和接口基础或者在学习fidder时遇到了问题,或许本文可以帮助到你 一.B/S端抓包 Fiddler设置 1. 官网下载fiddler ...