SQL Server进阶 窗口函数
序言
设计窗口函数目的?
在开窗函数出现之前存在着很多用 SQL 语句很难解决的问题,很多都要通过复杂的相关子查询或者存储过程来完成。
为了解决这些问题,在 2003 年 ISO SQL 标准加入了开窗函数,开窗函数的使用使得这些经典的难题可以被轻松的解决。
SQL Server 2012之后对窗口函数进行了极大的加强,但对于很多开发人员来说,对窗口函数却不甚了解,导致了这样强大的功能被浪费。
开窗函数可以优雅的部分取代分组查询和子查询。
什么是窗口函数?
可以看到与聚合函数不同的是,开窗函数在聚合函数后增加了一个 OVER 关键字。
开窗函数格式: 函数名(列) OVER(选项)
OVER 关键字表示把函数当成开窗函数而不是聚合函数。SQL 标准允许将所有聚合函数用做开窗函数,使用 OVER 关键字来区分这两种用法。
在上边的例子中,开窗函数 COUNT(*) OVER()对于查询结果的每一行都返回所有符合条件的行的条数。OVER 关键字后的括号中还经常添加选项用以改变进行聚合运算的窗口范围。如果 OVER 关键字后的括号中的选项为空,则开窗函数会对结果集中的所有行进行聚合运算。
窗口函数的典型范例是我们在SQL Server 2005之后用到的排序函数,比如代码清单1所示。
Row_Number() OVER (partition by xx ORDER BY xxx desc) RowNumber
因此,我们可以把窗口函数的语法抽象出来,如代码清单2所示。
函数() Over (PARTITION By 列1,列2,Order By 列3,窗口子句) AS 列别名
示例一
查询姓名,性别,该性别所有员工的总数
如果我们使用传统的写法,那一定会涉及到子查询,虽然使用子查询能够解决这个问题,但是子查询的使用非常麻烦,使用开窗函数则可以大大简化实现。
SELECT s.Sname,s.Ssex, (SELECT COUNT(*) FROM dbo.Student a WHERE a.Ssex=s.Ssex) AS GenderTotal FROM dbo.Student s
如果我们使用了窗口函数,代码瞬间就变得简洁,不再需要子查询或Join。
SELECT s.Sname,s.Ssex, COUNT(*) OVER (PARTITION BY Ssex) GenderTotal FROM dbo.Student s
查询结果

假如我们考虑更复杂的例子,在Over子句加上了Order By,来完成一个平均数累加,如果不使用窗口函数,那一定是游标,循环等麻烦的方式,如果使用了窗口函数,则一切就变得非常轻松。
Partition By
代码清单2展示了窗口函数的语法,其中Over子句之后第一个提到的就是Partition By。Partition By子句也可以称为查询分区子句,非常类似于Group By,都是将数据按照边界值分组,而Over之前的函数在每一个分组之内进行,如果超出了分组,则函数会重新计算,比如上图中的例子,我们将数据分为男性和女性两部分,前面的Count()函数针对这两组分别计算值(男性3,女性3)。
针对Partition By可以应用的函数不仅仅是我们所熟知的聚合函数,以及一些其他的函数,比如说Row_Number()。
示例二
SELECT [UserName]
,[Subject]
,[Score]
FROM [MyDataBase].[dbo].[StudentScores]
数据

SELECT [UserName] ,
[Subject] ,
[Score] ,
COUNT(*) OVER ( PARTITION BY UserName ) AS totalcount
FROM [MyDataBase].[dbo].[StudentScores];
COUNT(*) OVER ( PARTITION BY UserName ) AS totalcount

SELECT [UserName] ,
[Subject] ,
[Score] ,
COUNT(*) OVER ( PARTITION BY Subject ) AS totalcount
FROM [MyDataBase].[dbo].[StudentScores];
COUNT(*) OVER ( PARTITION BY Subject ) AS totalcount

SELECT [UserName] ,
[Subject] ,
[Score] ,
COUNT(*) OVER ( PARTITION BY Score ) AS totalcount
FROM [MyDataBase].[dbo].[StudentScores];
COUNT(*) OVER ( PARTITION BY Score ) AS totalcount

示例三
USE [MyDataBase]
GO
/****** Object: Table [dbo].[T_Person] Script Date: 2019/2/11 15:26:15 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[T_Person](
[FName] [varchar](20) NULL,
[FCity] [varchar](20) NULL,
[FAge] [int] NULL,
[FSalary] [int] NULL
) ON [PRIMARY] GO
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'Tom', N'BeiJing', 20, 3000)
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'Tim', N'ChengDu', 21, 4000)
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'Jim', N'BeiJing', 22, 3500)
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'Lily', N'London', 21, 2000)
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'John', N'NewYork', 22, 1000)
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'YaoMing', N'BeiJing', 20, 3000)
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'Swing', N'London', 22, 2000)
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'Guo', N'NewYork', 20, 2800)
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'YuQian', N'BeiJing', 24, 8000)
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'Ketty', N'London', 25, 8500)
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'Kitty', N'ChengDu', 25, 3000)
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'Merry', N'BeiJing', 23, 3500)
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'Smith', N'ChengDu', 30, 3000)
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'Bill', N'BeiJing', 25, 2000)
INSERT [dbo].[T_Person] ([FName], [FCity], [FAge], [FSalary]) VALUES (N'Jerry', N'NewYork', 24, 3300)
数据
比如我们想查询每个工资小于 5000 元的员工信息(城市以及年龄),并且在每行中都显示所有工资小于 5000 元的员工个数:
select fname,fcity,fsalary,(select count(*) from t_person where fsalary < 5000)
from dbo.t_person
where fsalary < 5000
使用子查询
select fname, fcity, fsalary, count(*) over()
from t_person
where fsalary < 5000
使用开窗函数

PARTITION BY 子句:
开窗函数的 OVER 关键字后括号中的可以使用 PARTITION BY 子句来定义行的分区来供进行聚合计算。与 GROUP BY 子句不同,PARTITION BY 子句创建的分区是独
立于结果集的,创建的分区只是供进行聚合计算的,而且不同的开窗函数所创建的分区也不互相影响。下面的 SQL 语句用于显示每一个人员的信息以及所属城市的人员数:
select fname,fcity,fage,fsalary,count(*) over(partition by fcity) 所在城市人数 from dbo.t_person
COUNT(*) OVER(PARTITION BY FCITY)表示对结果集按照FCITY进行分区,并且计算当前行所属的组的聚合计算结果。比如对于FName等于 Tom的行,它所属的城市是BeiJing,同属于BeiJing的人员一共有6个,所以对于这一列的显示结果为6。

在同一个SELECT语句中可以同时使用多个开窗函数,而且这些开窗函数并不会相互干扰。比如下面的SQL语句用于显示每一个人员的信息、所属城市的人员数以及同龄人的人数:
--显示每一个人员的信息、所属城市的人员数以及同龄人的人数:
select fname,
fcity,
fage,
fsalary,
count(*) over(partition by fcity) 所属城市的人个数,
count(*) over(partition by fage) 同龄人个数
from dbo.t_person

ORDER BY子句:
开窗函数中可以在OVER关键字后的选项中使用ORDER BY子句来指定排序规则,而且有的开窗函数还要求必须指定排序规则。使用ORDER BY子句可以对结果集按
照指定的排序规则进行排序,并且在一个指定的范围内进行聚合运算。ORDER BY子句的语法为:
ORDER BY 字段名 RANGE|ROWS BETWEEN 边界规则1 AND 边界规则2
RANGE表示按照值的范围进行范围的定义,而ROWS表示按照行的范围进行范围的定义;边界规则的可取值见下表:

“RANGE|ROWS BETWEEN 边界规则1 AND 边界规则2”部分用来定位聚合计算范围,这个子句又被称为定位框架。
例子程序一:查询从第一行到当前行的工资总和:
select fname,
fcity,
fage,
fsalary,
sum(fsalary) over(order by fsalary rows between unbounded preceding and current row) 到当前行工资求和
from dbo.t_person

这里的开窗函数“SUM(FSalary) OVER(ORDER BY FSalary ROWS BETWEEN
UNBOUNDED PRECEDING AND CURRENT ROW)”表示按照FSalary进行排序,然后计算从第
一行(UNBOUNDED PRECEDING)到当前行(CURRENT ROW)的和,这样的计算结果就是按照
工资进行排序的工资值的累积和。
“RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW”是开窗函数中最常使用的定位框架,为了简化使用,如果使用的是这种定位框架,则可以省略定位框架声明部分,也就是说上边的sql可以简化成:
select fname,
fcity,
fage,
fsalary,
sum(fsalary) over(order by fsalary)
from dbo.t_person
https://www.cnblogs.com/lihaoyang/p/6756956.html
优点
现在我们对窗口函数有了初步的概览,文章后我会提供一些具体的例子来让对窗口函数的概念更加深刻,窗口函数除了上面提到的输入行等于输出行之外,还有如下特性和好处:
1、类似Group By的聚合
2、非顺序的访问数据
3、可以对于窗口函数使用分析函数、聚合函数和排名函数
4、简化了SQL代码(消除Join)
5、消除中间表
窗口函数是整个SQL语句最后被执行的部分,这意味着窗口函数是在SQL查询的结果集上进行的,因此不会受到Group By, Having,Where子句的影响。
缺点
资料
https://blog.csdn.net/mzl87/article/details/84455076
https://www.imooc.com/article/25447
SQL Server进阶 窗口函数的更多相关文章
- SQL Server 进阶 01 数据库的设计
SQL Server 进阶 01 数据库的设计 本篇目录 课程内容回顾及介绍 为什么需要规范的数据库设计 设计数据库的步骤 绘制E-R(实体-关系)图 实体-关系模型 如何将E-R图转换为表 数据规范 ...
- 【目录】sql server 进阶篇系列
随笔分类 - sql server 进阶篇系列 sql server 下载安装标记 摘要: SQL Server 2017 的各版本和支持的功能 https://docs.microsoft.com/ ...
- SQL Server进阶(十二)常用函数
在SQL 2012基础教程中列出子句是按照以下顺序进行逻辑处理. FROM WHERE GROUP BY HAVING SELECT ORDER BY FROM TableName WHERE Use ...
- SQL Server进阶(十二)函数
概述 函数有且只有一个输入参数和一个返回值,而存储过程没有这个限制: 返回表变量的函数可以当做VIEW或者临时表用在WHERE/HAVING/SELECT/JOIN语句中而存储过程不可以: 存储过程中 ...
- SQL Server进阶(十一)临时表、表变量
临时表 本地临时表 适合开销昂贵 结果集是个非常小的集合 -- Local Temporary Tables IF OBJECT_ID('tempdb.dbo.#MyOrderTotalsByYe ...
- SQL Server进阶(五)子查询
概述 子查询的概念: 当一个查询是另一个查询的条件时,称之为子查询.子查询可以嵌套在主查询中所有位置,包括SELECT.FROM.WHERE.GROUP BY.HAVING.ORDER BY. 外面的 ...
- SQL Server进阶(六)表表达式--派生表、公用表表达式(CTE)、视图和内联表值函数
概述 表表达式是一种命名的查询表达式,代表一个有效地关系表.可以像其他表一样,在数据处理中使用表表达式. SQL Server支持四种类型的表表达式:派生表,公用表表达式,视图和内联表值函数. 为什么 ...
- SQL Server进阶(七)集合运算
概述 为什么使用集合运算: 在集合运算中比联接查询和EXISTS/NOT EXISTS更方便. 并集运算(UNION) 并集:两个集合的并集是一个包含集合A和B中所有元素的集合. 在T-SQL中.UN ...
- SQL Server进阶(二)字段类型
概述 系统数据类型详情 SqlDbType namespace System.Data { // // 摘要: // 指定要用于 System.Data.SqlClient.SqlParameter ...
随机推荐
- [luogu2051][bzoj1801][AHOI2009]chess中国象棋【动态规划】
题目描述 这次小可可想解决的难题和中国象棋有关,在一个N行M列的棋盘上,让你放若干个炮(可以是0个),使得没有一个炮可以攻击到另一个炮,请问有多少种放置方法.大家肯定很清楚,在中国象棋中炮的行走方式是 ...
- ELK部署详解--logstash
logstash.yml # Settings file in YAML## Settings can be specified either in hierarchical form, e.g.:# ...
- [NOI2010]航空管制(拓扑排序+贪心)
题目描述 世博期间,上海的航空客运量大大超过了平时,随之而来的航空管制也频频发生.最近,小X就因为航空管制,连续两次在机场被延误超过了两小时.对此,小X表示很不满意. 在这次来烟台的路上,小X不幸又一 ...
- Hadoop集群的构建和安装
1.安装Java $ yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel 上述命令默认安装位置/usr/lib/jvm/java-1. ...
- Centos 7下下载和安装docker
sudo yum install -y device-mapper sudo modprobe dm_mod ls -l /sys/class/misc/device-mapper sudo rpm ...
- poj3190 Stall Reservations
我一开始想用线段树,但是发现还要记录每头cow所在的棚...... 无奈之下选择正解:贪心. 用priority_queue来维护所有牛棚中结束时间最早的那个牛棚,即可得出答案. 注意代码实现的细节. ...
- 【洛谷P1637】三元上升子序列
题目大意:给定一个长度为 N 的序列,求有多少个三元组满足 \(i<j<k,a_i<a_j<a_k\). 题解:这是一类二维偏序问题,与逆序对问题类似. 对于序列中每个点来说, ...
- [luogu5077][Tweetuzki 爱等差数列]
题目链接 思路 数学题 首先列出等差数列求和的式子. \[S = \frac{(n + m)(n - m + 1)}{2}(n为末项,m为首项)\] \[S * 2= (n + m)(n - m + ...
- binutils安装
mkdir /tools/binutils -p //新建一个安装目录 .configure prefix=/tools/binutils make make install binutils工具就安 ...
- Hibernate3 多对多关系
学习hibernate的笔迹第三篇,主要内容:多对多关系, 检索策略,HQL查询,QBC,事物管理,c3p0配置 1.1.1 Hibernate的关联关系映射:(多对多) 1.1.1.1 多 ...