做为一名开发人员来说,我感受在职场白混了好多年,多是本身真的没有进取的精神吧,看了《程序员的SQL金典》这本电子书,真的让我学到了很多知识,真心喜欢这本电子书,书中讲解的内容比较好懂,也比较实用。谢谢做者的辛勤汗水:)。php
今天将要介绍SQL Sever的开窗函数,何谓开窗函数,不懂吧。反正对于我来讲,我是摸不着头脑了,第一次据说过。那么,什么是开窗函数,其实能够理解为是聚合函数的一个增强版。由于使用聚合函数的话(不包括子查询的状况),整个查询都只能是聚合列返回值,而不能有基础行的返回值。那么对于须要基础行的返回值的话,就须要使用复杂的子查询或者是存储过程等才能够解决。可是使用开窗函数就能够轻松解决,它可以在同一行中同时返回基础行的列和聚合列。在ISO SQL规定了这样的函数为开窗函数,在Oracle中则被称为分析函数,而在DB2中则被称为OLAP函数。程序员
开窗函数与聚合函数同样,都是对行的集合组进行聚合计算。它用于为行定义一个窗口(这里的窗口是指运算将要操做的行的集合),它对一组值进行操做,不须要使用GROUP BY子句对数据进行分组,可以在同一行中同时返回基础行的列和聚合列。反正我理解这个函数已经使用好子查询或者是其它方式求得聚合列的值给我合并。函数
以书中的例子一步一步来介绍,假设要计算全部人员的总数,咱们能够执行下面的SQL语句:学习
SELECT COUNT(FName) FROM T_Person
这种方式比较直接,只返回一个聚合列的值,没有任何基础行的列的值。可是有时须要从不在聚合函数中的行的列中访问这些聚合计算的值(即基础行的列)。好比咱们想查询每一个工资小于5000元的员工信息(城市以及年龄),而且在每行中都显示全部工资小于5000 元的员工个数,尝试编写下面的SQL语句:spa
SELECT FName, FCITY, FAGE, FSalary, COUNT(FName) FROM T_Person WHERE FSALARY<5000
执行上面的SQL之后咱们会获得下面的错误信息:
选择列表中的列'T_Person.FCity' 无效,由于该列没有包含在聚合函数或GROUP BY 子句中。
这是由于全部不包含在聚合函数中的列必须声明在GROUP BY 子句中,使用子查询的方式是能够解决:code
SELECT FName, FCITY, FAGE, FSalary, ( SELECT COUNT(FName) FROM T_Person WHERE FSALARY<5000 ) FROM T_Person WHERE FSALARY<5000
虽然使用子查询可以解决这个问题,可是子查询的使用很是麻烦,使用开窗函数则能够大大简化实现,下面的SQL语句展现了若是使用开窗函数来实现一样的效果:blog
SELECT FName, FCITY, FAGE, FSalary, COUNT(FName) OVER() FROM T_Person WHERE FSALARY<5000
能够看到与聚合函数不一样的是,开窗函数在聚合函数后增长了一个OVER关键字。
开窗函数的调用格式为:函数名(列) OVER(选项)排序
我这里使用的是SQL Server 2008 R2,不知道从何时开始,SQL SERVER也支持开窗函数中使用ORDER BY子句(注:书本中说MSSQLServer中是不支持开窗函数中使用ORDER BY子句)。无论怎么样,这里我整合了网上的相关内容。也正由于开窗函数支持了ORDER BY子句以后,开窗函数被分为两大类。开发
第一大类:聚合开窗函数====》聚合函数(列) OVER (选项),这里的选项能够是PARTITION BY子句,但不但是ORDER BY子句get
第二大类:排序开窗函数====》排序函数(列) OVER(选项),这里的选项能够是ORDER BY子句,也能够是 OVER(PARTITION BY子句 ORDER BY子句),但不能够是PARTITION BY子句
聚合开窗函数 |
OVER 关键字表示把聚合函数当成聚合开窗函数而不是聚合函数。SQL 标准容许将全部聚合函数用作聚合开窗函数。
在上边的例子中,开窗函数COUNT(*) OVER()对于查询结果的每一行都返回全部符合条件的行的条数。OVER关键字后的括号中还常常添加选项用以改变进行聚合运算的窗口范围。若是OVER关键字后的括号中的选项为空,则开窗函数会对结果集中的全部行进行聚合运算。
PARTITION BY 子句 |
开窗函数的OVER关键字后括号中的可使用PARTITION BY 子句来定义行的分区来供进行聚合计算。与GROUP BY 子句不一样,PARTITION BY 子句建立的分区是独立于结果集的,建立的分区只是供进行聚合计算的,并且不一样的开窗函数所建立的分区也不互相影响。下面的SQL语句用于显示每个人员的信息以及所属城市的人员数:
SELECT FName, FCITY, FAGE, FSalary, COUNT(FName) OVER(PARTITION BY FCITY) FROM T_Person
OVER(PARTITION BY FCITY)表示对结果集按照FCITY进行分区,而且计算当前行所属的组的聚合计算结果。在同一个SELECT语句中能够同时使用多个开窗函数,并且这些开窗函数并不会相互干扰。好比下面的SQL语句用于显示每个人员的信息、所属城市的人员数以及同龄人的人数:
SELECT FName,FCITY, FAGE, FSalary, COUNT(FName) OVER(PARTITION BY FCITY), COUNT(FName) OVER(PARTITION BY FAGE) FROM T_Person
排序开窗函数 |
对于排序开窗函数来说,它支持的开窗函数分别为:ROW_NUMBER(行号)、RANK(排名)、DENSE_RANK(密集排名)和NTILE(分组排名)。
先看一段SQL语句:
select FName, FSalary, FCity, FAge, row_number() over(order by FSalary) as rownum, rank() over(order by FSalary) as rank, dense_rank() over(order by FSalary) as dense_rank, ntile(6) over(order by FSalary)as ntile from T_Person order by FName
执行的结果以下(对于想本身尝试的朋友,那你得辛苦点,下载电子书或者是购买书来学习吧。由于我但是限于篇幅,省略去大部份内容哦):
看到上面的结果了吧,下面来介绍下相关的内容。咱们获得的最终结果是按照FName进行升序显示的。
对于row_number() over(order by FSalary) as rownum来讲,这个排序开窗函数是按FSalary升序的方式来排序,并得出排序结果的序号
对于rank() over(order by FSalary) as rank来讲,这个排序形容函数是按FSalary升序的方式来排序,并得出排序结果的排名号。这个函数求出来的排名结果能够排列,并列排名以后的排名将是并列的排名加上并列数(简单说每一个人只有一种排名,而后出现两个并列第一名的状况,这时候排在两个第一名后面的人将是第三名,也就是没有了第二名,可是有两个第一名)
对于dense_rank() over(order by FSalary) as dense_rank来讲,这个排序函数是按FSalary升序的方式来排序,并得出排序结果的排名号。这个函数与rand()函数不一样在于,并列排名以后的排名只是并列排名加1(简单说每一个人只有一种排名,而后出现两个并列第一名的状况,这时候排在两个第一名后面的人将是第二名,也就是两个第一名,一个第二名)
对于ntile(6) over(order by FSalary)as ntile 来讲,这个排序函数是按FSalary升序的方式来排序,并得出排序结果的分组数。
排序函数和聚合开窗函数相似,也支持在OVER子句中使用PARTITION BY语句。例如:
select FName, FSalary, FCity, FAge, row_number() over(partition by FName order by FSalary) as rownum, rank() over(partition by FName order by FSalary) as rank, dense_rank() over(partition by FName order by FSalary) as dense_rank, ntile(6) over(partition by FName order by FSalary)as ntile from T_Person order by FName
关于PARTITION BY子句,请看上面的介绍,这里就再也不累赘了。可是须要注意一点的是,在排序开窗函数中使用PARTITION BY子句须要放置在ORDER BY子句以前。
至此本文完。