(011)每日SQL学习:SQL开窗函数
开窗函数:在开窗函数出现之前存在着很多用 SQL 语句很难解决的问题,很多都要通过复杂的相关子查询或者存储过程来完成。为了解决这些问题,在 2003 年 ISO SQL 标准加入了开窗函数,开窗函数的使用使得这些经典的难题可以被轻松的解决。目前在 MSSQLServer、Oracle、DB2 等主流数据库中都提供了对开窗函数的支持,不过非常遗憾的是 MYSQL 暂时还未对开窗函数给予支持。
开窗函数简介:与聚合函数一样,开窗函数也是对行集组进行聚合计算,但是它不像普通聚合函数那样每组只返回一个值,开窗函数可以为每组返回多个值,因为开窗函数所执行聚合计
算的行集组是窗口。在 ISO SQL 规定了这样的函数为开窗函数,在 Oracle 中则被称为分析函数。
数据表(Oracle):T_Person 表保存了人员信息,FName 字段为人员姓名,FCity 字段为人员所在的城市名,FAge 字段为人员年龄,FSalary 字段为人员工资
CREATE TABLE T_Person (FName VARCHAR2(20),FCity VARCHAR2(20),FAge INT,FSalary INT)
向 T_Person 表中插入一些演示数据:
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘Tom‘,‘BeiJing‘,20,3000);
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘Tim‘,‘ChengDu‘,21,4000);
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘Jim‘,‘BeiJing‘,22,3500);
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘Lily‘,‘London‘,21,2000);
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘John‘,‘NewYork‘,22,1000);
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘YaoMing‘,‘BeiJing‘,20,3000);
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘Swing‘,‘London‘,22,2000);
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘Guo‘,‘NewYork‘,20,2800);
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘YuQian‘,‘BeiJing‘,24,8000);
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘Ketty‘,‘London‘,25,8500);
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘Kitty‘,‘ChengDu‘,25,3000);
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘Merry‘,‘BeiJing‘,23,3500);
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘Smith‘,‘ChengDu‘,30,3000);
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘Bill‘,‘BeiJing‘,25,2000);
INSERT INTO T_Person(FName,FCity,FAge,FSalary)
VALUES(‘Jerry‘,‘NewYork‘,24,3300);
select * from t_person:

要计算所有人员的总数,我们可以执行下面的 SQL 语句:SELECT COUNT(*) FROM T_Person
除了这种较简单的使用方式,有时需要从不在聚合函数中的行中访问这些聚合计算的值。比如我们想查询每个工资小于 5000 元的员工信息(城市以及年龄),并且在每行中都显示所有工资小于 5000 元的员工个数:
select fname,
fcity,
fsalary,
(select count(*) from t_person where fsalary < 5000) 工资少于5000员工总数
from t_person
where fsalary < 5000

虽然使用子查询能够解决这个问题,但是子查询的使用非常麻烦,使用开窗函数则可以大大简化实现,下面的 SQL 语句展示了如果使用开窗函数来实现同样的效果:
select fname, fcity, fsalary,5000
可以看到与聚合函数不同的是,开窗函数在聚合函数后增加了一个 OVER 关键字。
分区,并且计算当前行所属的组的聚合计算结果。比如对于FName等于 Tom的行,它所属的城市是BeiJing,同
属于BeiJing的人员一共有6个,所以对于这一列的显示结果为6。
这就不需要先对fcity分组求和,然后再和t_person表连接查询了,省事儿。

在同一个SELECT语句中可以 ORDER BY子句:
开窗函数中可以在OVER关键字后的选项中使用1000+2000+2000+2000 ”)。
下边这的估计不常用:
例子程序三:
SELECT FName,
FSalary,
SUM(FSalary) OVER(ORDER BY FSalary ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING) 前二后二和
FROM T_Person;

这里的开窗函数“SUM(FSalary) OVER(ORDER BY FSalary ROWS BETWEEN 2
PRECEDING AND 2 FOLLOWING)”表示按照FSalary进行排序,然后计算从当前行前两行(2
PRECEDING)到当前行后两行(2 FOLLOWING)的工资和,注意对于第一条和第二条而言它们
的“前两行”是不存在或者不完整的,因此计算的时候也是要按照前两行是不存在或者不完整进
行计算,同样对于最后两行数据而言它们的“后两行”也不存在或者不完整的,同样要进行类似
的处理。
例子程序四:
SELECT FName, FSalary,
SUM(FSalary) OVER(ORDER BY FSalary ROWS BETWEEN 1 FOLLOWING AND 3 FOLLOWING) 后面一到三之和
FROM T_Person;

这里的开窗函数“SUM(FSalary) OVER(ORDER BY FSalary ROWS BETWEEN 1
FOLLOWING AND 3 FOLLOWING)”表示按照FSalary进行排序,然后计算从当前行后一行(1
FOLLOWING)到后三行(3 FOLLOWING)的工资和。注意最后一行没有后续行,其计算结果为
空值NULL而非0。
例子程序五:算工资排名
SELECT FName, FSalary,
COUNT(*) OVER(ORDER BY FSalary ROWS BETWEEN UNBOUNDED PRECEDING AND
CURRENT ROW)
FROM T_Person;

这里的开窗函数“COUNT(*) OVER(ORDER BY FSalary RANGE BETWEEN UNBOUNDED
PRECEDING AND CURRENT ROW)”表示按照FSalary进行排序,然后计算从第一行
(UNBOUNDED PRECEDING)到当前行(CURRENT ROW)的人员的个数,这个可以看作是计算
人员的工资水平排名。
不再用ROWNUM 了 省事了。这个over简写就会出错。
例子程序6:结合max求到目前行的最大值
SELECT FName, FSalary,FAge,
MAX(FSalary) OVER(ORDER BY FAge) 此行之前最大值
FROM T_Person;

这里的开窗函数“MAX(FSalary) OVER(ORDER BY FAge)”是“MAX(FSalary)
OVER(ORDER BY FAge RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)”
的简化写法,它表示按照FSalary进行排序,然后计算从第一行(UNBOUNDED PRECEDING)
到当前行(CURRENT ROW)的人员的最大工资值。
例子程序6:over(partition by XX order by XX) partition by和order by 结合
员工信息+同龄人最高工资,按工资排序
SELECT FName, FSalary,FAge,
MAX(FSalary) OVER(PARTITION BY FAge ) 同龄人最高工资
FROM T_Person;
--原文写法
SELECT FName, FSalary,FAge,
MAX(FSalary) OVER(PARTITION BY FAge order by Fsalsry ) 同龄人最高工资
FROM T_Person;
--个人觉得显示有点问题,order by 之后最高工资就变成了每个人的工资显示。并不是同龄人的最高工资显示

PARTITION BY子句和ORDER BY 可以 共 同 使用,从 而 可以 实现 更 加复 杂 的 功能。
原文地址:http://www.mamicode.com/info-detail-1774214.html
(011)每日SQL学习:SQL开窗函数的更多相关文章
- SQL SERVER:开窗函数 SUM() OVER() 数据统计中一例使用
由于前一段时间胃痛,导致博客园博客都停更了一个月左右.近几天,胃病终于稍微有所好转,决定重新写博文. 前几天,有个朋友刚好问到本人有关 SQL 语句,大致是原表有两列,分别为月份.月份销售额,而需要一 ...
- sql语句中开窗函数的使用
开窗函数主要分为2类: 1.排序开窗函数: rank() over(partition by xxx order by yyy) //各分区按照yyy字段排序,如果yyy字段值一样,则rank值一样, ...
- SQL Server 2012 - 开窗函数
-- 开窗函数:在结果集的基础上进一步处理(聚合操作) -- Over函数,添加一个字段显示最大年龄 SELECT * , MAX(StuAge) OVER ( ) MaxStuAge FROM db ...
- SQL学习_时间函数
最近测试报表需要统计不同时间段的列表记录,收集一些时间函数作为参考,原文地址:http://blog.csdn.net/lyzlyfok/article/details/6282509 sql ser ...
- SQL学习笔记:函数
SQL函数 AVG select AVG(col) AS avgvalue from tablename select col2 from tablename where col1>(selec ...
- sql server利用开窗函数over() 进行分组统计
这是一道常见的面试题,在实际项目中经常会用到. 需求:求出以产品类别为分组,各个分组里价格最高的产品信息. 实现过程如下: declare @t table( ProductID int, Produ ...
- PL/SQL学习笔记之函数
一:函数 函数与过程的最大不同就是,函数有返回值.适用于需要返回结果的场景. 二:创建函数 CREATE [OR REPLACE] FUNCTION function_name [(parameter ...
- SQL学习记录:函数(二)
字符串函数 1.获取字符的ASCII码 语法结构: ASCII(espression) 这里的expression是一个返回char或varchar数据类型的表达式,ASCII函数仅对表达式最左 ...
- SQL学习-SQL Server
- SQL SERVER开窗函数
作为一名开发人员来讲,我感觉在职场白混了好多年,可能是自己真的没有进取的精神吧,看了<程序员的SQL金典>这本电子书,真的让我学到了不少知识,真心喜欢这本电子书,书中讲解的内容比较好懂,也 ...
随机推荐
- hibernate连接数据库中文乱码
4.做完这两步还是不行,需要修改hibernate的配置文件hibernate.cfg.xml,在配置文件配置hibernate.connection.url属性.示例: <property n ...
- Class的一些使用技巧?
1.forName和newInstance结合起来使用,可以根据存储在字符串中的类名创建对象.例如 Object obj = Class.forName(s).newInstance(); 2.虚拟机 ...
- Spring Boot的自动配置
Spring Boot的自动配置 --摘自https://www.hollischuang.com/archives/1791 随着Ruby.Groovy等动态语言的流行,相比较之下Java的开发显得 ...
- Spring Cloud Hystrix应用篇(十一)
一.背景 分布式系统环境下,服务间类似依赖非常常见,一个业务调用通常依赖多个基础服务.如下图,对于同步调用,当库存服务不可用时,商品服务请求线程被阻塞,当有大批量请求调用库存服务时,最终可能导致整个商 ...
- Miller Rabin素数检测与Pollard Rho算法
一些前置知识可以看一下我的联赛前数学知识 如何判断一个数是否为质数 方法一:试除法 扫描\(2\sim \sqrt{n}\)之间的所有整数,依次检查它们能否整除\(n\),若都不能整除,则\(n\)是 ...
- 经典项目管理 OR 敏捷项目管理,我该怎么选?
CODING 项目协同近期为支持传统项目管理推出了「经典项目管理」.至此,CODING 已全面支持敏捷项目管理以及传统项目管理.那么问题来了,「经典项目管理」和「敏捷项目管理」,我该怎么选呢?本文将从 ...
- Java 使用线程池执行若干任务
在执行一系列带有IO操作(例如下载文件),且互不相关的异步任务时,采用多线程可以很极大的提高运行效率.线程池包含了一系列的线程,并且可以管理这些线程.例如:创建线程,销毁线程等.本文将介绍如何使用Ja ...
- 转载 - Ubuntu源改国内源 与 批量更改ubuntu机器apt源
change_apt_source.sh # !/bin/bash # 备份原来的源文件 cp /etc/apt/sources.list /etc/apt/sources.list.bak # 获取 ...
- nginx日志按天切割
要求:以天为单位进行日志文件的切割,如host.access_20150915.log, 日志保留最近10天的, 超过10天的日志文件则进行删除. nginxcutlogs.sh脚本内容: #!/bi ...
- 深入研究.NET 5的开放式遥测
OpenTelemetry 介绍 OpenTelemetry是一种开放的源代码规范,工具和SDK,用于检测,生成,收集和导出遥测数据(指标,日志和跟踪),开放遥测技术得到了Cloud Native C ...