从SQL Server 2005起,SQL Server开始支持窗口函数 (Window Function),以及到SQL Server 2012,窗口函数功能增强,目前为止支持以下几种窗口函数:

1. 排序函数 (Ranking Function) ;

2. 聚合函数 (Aggregate Function) ;

3. 分析函数 (Analytic Function) ;

4. NEXT VALUE FOR Function, 这是给sequence专用的一个函数;

. 排序函数(Ranking Function)

帮助文档里的代码示例很全。

排序函数中,ROW_NUMBER()较为常用,可用于去重、分页、分组中选择数据,生成数字辅助表等等;

排序函数在语法上要求OVER子句里必须含ORDER BY,否则语法不通过,对于不想排序的场景可以这样变通;

drop table if exists test_ranking

create table test_ranking
(
id int not null,
name varchar(20) not null,
value int not null
) insert test_ranking
select 1,'name1',1 union all
select 1,'name2',2 union all
select 2,'name3',2 union all
select 3,'name4',2 select id , name, ROW_NUMBER() over (PARTITION by id ORDER BY name) as num
from test_ranking select id , name, ROW_NUMBER() over (PARTITION by id) as num
from test_ranking
/*
Msg 4112, Level 15, State 1, Line 1
The function 'ROW_NUMBER' must have an OVER clause with ORDER BY.
*/ --ORDERY BY后面给一个和原表无关的派生列
select id , name, ROW_NUMBER() over (PARTITION by id ORDER BY GETDATE()) as num
from test_ranking select id , name, ROW_NUMBER() over (PARTITION by id ORDER BY (select 0)) as num
from test_ranking

. 聚合函数 (Aggregate Function)

SQL Server 2005中,窗口聚合函数仅支持PARTITION BY,也就是说仅能对分组的数据整体做聚合运算;

SQL Server 2012开始,窗口聚合函数支持ORDER BY,以及ROWS/RAGNE选项,原本需要子查询来实现的需求,如: 移动平均 (moving averages), 总计聚合 (cumulative aggregates), 累计求和 (running totals) 等,变得更加方便;

代码示例1:总计/小计/累计求和

drop table if exists test_aggregate;

create table test_aggregate
(
event_id varchar(100),
rk int,
price int
) insert into test_aggregate
values
('a',1,10),
('a',2,10),
('a',3,50),
('b',1,10),
('b',2,20),
('b',3,30) --1. 没有窗口函数时,用子查询
select a.event_id,
a.rk, --build ranking column if needed
a.price,
   (select sum(price) from test_aggregate b where b.event_id = a.event_id and b.rk <= a.rk) as totalprice
from test_aggregate a --2. 从SQL Server 2012起,用窗口函数
--2.1
--没有PARTITION BY, 没有ORDER BY,为全部总计;
--只有PARTITION BY, 没有ORDER BY,为分组小计;
--只有ORDER BY,没有PARTITION BY,为全部累计求和(RANGE选项,见2.2)
select *,
   sum(price) over() as TotalPrice,
   sum(price) over(partition by event_id) as SubTotalPrice,
sum(price) over(order by rk) as RunningTotalPrice
from test_aggregate a --2.2 注意ORDER BY列的选择,可能会带来不同结果
select *,
   sum(price) over(partition by event_id order by rk) as totalprice
from test_aggregate a
/*
event_id rk price totalprice
a 1 10 10
a 2 10 20
a 3 50 70
b 1 10 10
b 2 20 30
b 3 30 60
*/ select *,
   sum(price) over(partition by event_id order by price) as totalprice
from test_aggregate a
/*
event_id rk price totalprice
a 1 10 20
a 2 10 20
a 3 50 70
b 1 10 10
b 2 20 30
b 3 30 60
*/ --因为ORDER BY还有个子选项ROWS/RANGE,不指定的情况下默认为RANGE UNBOUNDED PRECEDING AND CURRENT ROW
--RANGE按照ORDER BY中的列值,将相同的值的行均视为当前同一行
select *,sum(price) over(partition by event_id order by price) as totalprice from test_aggregate a
select *,sum(price) over(partition by event_id order by price range between unbounded preceding and current row) as totalprice from test_aggregate a --如果ORDER BY中的列值有重复值,手动改用ROWS选项即可实现逐行累计求和
select *,sum(price) over(partition by event_id order by price rows between unbounded preceding and current row) as totalprice from test_aggregate a

代码示例2:移动平均

--移动平均,举个例子,就是求前N天的平均值,和股票市场的均线类似
drop table if exists test_moving_avg create table test_moving_avg
(
ID int,
Value int,
DT datetime
) insert into test_moving_avg
values
(1,10,GETDATE()-10),
(2,110,GETDATE()-9),
(3,100,GETDATE()-8),
(4,80,GETDATE()-7),
(5,60,GETDATE()-6),
(6,40,GETDATE()-5),
(7,30,GETDATE()-4),
(8,50,GETDATE()-3),
(9,20,GETDATE()-2),
(10,10,GETDATE()-1) --1. 没有窗口函数时,用子查询
select *,
(select AVG(Value) from test_moving_avg a where a.DT >= DATEADD(DAY, -5, b.DT) AND a.DT < b.DT) AS avg_value_5days
from test_moving_avg b --2. 从SQL Server 2012起,用窗口函数
--三个内置常量,第一行,最后一行,当前行:UNBOUNDED PRECEDING, UNBOUNDED FOLLOWING, CURRENT ROW
--在行间移动,用BETWEEN m preceding AND n following (m, n > 0)
SELECT *,
sum(value) over (ORDER BY DT ROWS BETWEEN 5 preceding AND CURRENT ROW) moving_sum,
avg(value) over (ORDER BY DT ROWS BETWEEN 4 preceding AND CURRENT ROW) moving_avg1,
avg(value) over (ORDER BY DT ROWS BETWEEN 5 preceding AND 1 preceding) moving_avg2,
avg(value) over (ORDER BY DT ROWS BETWEEN 3 preceding AND 1 following) moving_avg3
FROM test_moving_avg
ORDER BY DT

. 分析函数 (Analytic Function)

代码示例1:取当前行某列的前一个/下一个值

drop table if exists test_analytic

create table test_analytic
(
SalesYear varchar(10),
Revenue int,
Offset int
) insert into test_analytic
values
(2013,1001,1),
(2014,1002,1),
(2015,1003,1),
(2016,1004,1),
(2017,1005,1),
(2018,1006,1) --当年及去年的销售额
select *,lag(Revenue,1,null) over(order by SalesYear asc) as PreviousYearRevenue from test_analytic
select *,lag(Revenue,Offset,null) over(order by SalesYear asc) as PreviousYearRevenue from test_analytic
select *,lead(Revenue,1,null) over(order by SalesYear desc) as PreviousYearRevenue from test_analytic --当年及下一年的销售额
select *,lead(Revenue,1,null) over(order by SalesYear asc) as NextYearRevenue from test_analytic
select *,lead(Revenue,Offset,null) over(order by SalesYear asc) as NextYearRevenue from test_analytic
select *,lag(Revenue,1,null) over(order by SalesYear desc) as NextYearRevenue from test_analytic --可以根据offset调整跨度

代码示例2:分组中某列最大/最小值,对应的其他列值

假设有个门禁系统,在员工每次进门时写入一条记录,记录了“身份号码”,“进门时间”,“衣服颜色",查询每个员工最后一次进门时的“衣服颜色”。

drop table if exists test_first_last

create table test_first_last
(
EmployeeID int,
EnterTime datetime,
ColorOfClothes varchar(20)
) insert into test_first_last
values
(1001, GETDATE()-9, 'GREEN'),
(1001, GETDATE()-8, 'RED'),
(1001, GETDATE()-7, 'YELLOW'),
(1001, GETDATE()-6, 'BLUE'),
(1002, GETDATE()-5, 'BLACK'),
(1002, GETDATE()-4, 'WHITE') --1. 用子查询
--LastColorOfColthes
select * from test_first_last a
where not exists(select 1 from test_first_last b where a.EmployeeID = b.EmployeeID and a.EnterTime < b.EnterTime) --LastColorOfColthes
select *
from
(select *, ROW_NUMBER() over(partition by EmployeeID order by EnterTime DESC) num
from test_first_last ) t
where t.num =1 --2. 用窗口函数
--用LAST_VALUE时,必须加上ROWS/RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING,否则结果不正确
select *,
FIRST_VALUE(ColorOfClothes) OVER (PARTITION BY EmployeeID ORDER BY EnterTime DESC) as LastColorOfClothes,
FIRST_VALUE(ColorOfClothes) OVER (PARTITION BY EmployeeID ORDER BY EnterTime ASC) as FirstColorOfClothes,
LAST_VALUE(ColorOfClothes) OVER (PARTITION BY EmployeeID ORDER BY EnterTime ASC ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) as LastColorOfClothes,
LAST_VALUE(ColorOfClothes) OVER (PARTITION BY EmployeeID ORDER BY EnterTime DESC ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) as FirstColorOfClothes
from test_first_last --对于显示表中所有行,并追加Last/First字段时用窗口函数方便些
--对于挑选表中某一行/多行时,用子查询更方便

. NEXT VALUE FOR Function

drop sequence if exists test_seq

create sequence test_seq
start with 1
increment by 1; GO drop table if exists test_next_value create table test_next_value
(
ID int,
Name varchar(10)
) insert into test_next_value(Name)
values
('AAA'),
('AAA'),
('BBB'),
('CCC') --对于多行数据获取sequence的next value,是否使用窗口函数都会逐行计数
--窗口函数中ORDER BY用于控制不同列值的计数顺序
select *, NEXT VALUE FOR test_seq from test_next_value
select *, NEXT VALUE FOR test_seq OVER(ORDER BY Name DESC) from test_next_value

参考:

SELECT - OVER Clause (Transact-SQL)

https://docs.microsoft.com/en-us/sql/t-sql/queries/select-over-clause-transact-sql?view=sql-server-2017

SQL Server Windowing Functions: ROWS vs. RANGE

https://www.sqlpassion.at/archive/2015/01/22/sql-server-windowing-functions-rows-vs-range/

16. 窗口函数 (Window Function) 的使用的更多相关文章

  1. SQL Server Window Function 窗体函数读书笔记一 - SQL Windowing

    SQL Server 窗体函数主要用来处理由 OVER 子句定义的行集, 主要用来分析和处理 Running totals Moving averages Gaps and islands 先看一个简 ...

  2. SQL Server Window Function 窗体函数读书笔记二 - A Detailed Look at Window Functions

    这一章主要是介绍 窗体中的 Aggregate 函数, Rank 函数, Distribution 函数以及 Offset 函数. Window Aggregate 函数 Window Aggrega ...

  3. Calling a parent window function from an iframe

    I want to call a parent window JavaScript function from an iframe. <script>function abc(){ ale ...

  4. PostgreSQL 窗口函数 ( Window Functions ) 如何使用?

    一.为什么要有窗口函数 我们直接用例子来说明,这里有一张学生考试成绩表testScore: 现在有个需求,需要查询的时候多出一列subject_avg_score,为此科目所有人的平均成绩,好跟每个人 ...

  5. 翻译:window function(已提交到MariaDB官方手册)

    本文为mariadb官方手册:window functions的译文. 原文:https://mariadb.com/kb/en/window-functions-overview/ 我提交到Mari ...

  6. Flink 的Window 操作(基于flink 1.3描述)

    Window是无限数据流处理的核心,Window将一个无限的stream拆分成有限大小的”buckets”桶,我们可以在这些桶上做计算操作.本文主要聚焦于在Flink中如何进行窗口操作,以及程序员如何 ...

  7. Flink中的window、watermark和ProcessFunction

    一.Flink中的window 1,window简述  window 是一种切割无限数据为有限块进行处理的手段.Window 是无限数据流处理的核心,Window 将一个无限的 stream 拆分成有 ...

  8. 详解SQL操作的窗口函数

    摘要:窗口函数是聚集函数的延伸,是更高级的SQL语言操作,主要用于AP场景下对数据进行一些分析.汇总.排序的功能. 本文分享自华为云社区<GaussDB(DWS) SQL进阶之SQL操作之窗口函 ...

  9. temp表空间被过多占用处理方法

    这个步骤比较简单,查询v$sort_usage就可以了: (select username,session_addr,sql_id,contents,segtype,blocks*8/1024/102 ...

随机推荐

  1. Java1.8 JDK源码中,对两个类进行 按位与 操作是什么意思

    Java容器类库中的Map接口(java\util\Map.java)中有一个Entry接口(java\util\Map.java),其中有几个接口方法用到了类和类的按位与操作,即类和类之间有 &am ...

  2. Linux-(type,vim)

    type命令 1.命令格式: type [参数][命令] 2.命令功能: 使用 type 命令轻松找出给定的命令是否是别名.shell 内置命令.文件.函数或关键字.也可以找到命令的实际路径. 3.命 ...

  3. 如何做实时监控?—— 参考 Spring Boot 实现(转)

    转自:http://blog.csdn.net/xiaoyu411502/article/details/48129057 随着 微服务 的流行,相比较以前一个大型应用程序搞定所有需求,我们现在更倾向 ...

  4. Leetcode 746. Min Cost Climbing Stairs

    思路:动态规划. class Solution { //不能对cost数组进行写操作,因为JAVA中参数是引用 public int minCostClimbingStairs(int[] cost) ...

  5. 白话js this指向问题

    前言   通过本文,你大概能了解this基础指向的问题,抛开例子去说this太虚幻,这里还是结合几篇博文做个整理,算是个人的记录了. 先说概念,this指向与申明无关,永远指向距离自己最近的最终调用者 ...

  6. springMVC实现 MultipartFile 多文件上传

    1.Maven引入所需的 jar 包(或自行下载) <dependency> <groupId>commons-io</groupId> <artifactI ...

  7. shell命令——cut

    功能:把行分成域 默认限定符为tab, -d:改变限定符 -f:指定输出力包含的域

  8. Mac添加命令别名

    1. 切换到用户主目录 $ cd 2. 编辑或新建.bash_profile文件 3. 添加别名 命令别名设置语法: alias [别名]='[指令名称]' 注意:等号两边均无空格,指令名称中如有空格 ...

  9. 并发编程 —— Timer 源码分析

    前言 在平时的开发中,肯定需要使用定时任务,而 Java 1.3 版本提供了一个 java.util.Timer 定时任务类.今天一起来看看这个类. 1.API 介绍 Timer 相关的有 3 个类: ...

  10. 树莓派安装.net core 2.1

    0.更新源 sudo apt-get update 1.安装依赖 sudo apt-get install curl libunwind8 gettext 2.下载 SDK 或者 RunTime ht ...