窗口和窗口函數


對於Select子句查詢的結果集,可以按照指定的字段進行分區,如下圖所示,按照Province字段來對查詢的結果集進行分區,可以認為,每一個分區就是一個窗口,因此,窗口是數據行的集合,是Select查詢結果集的一個子集。

在TSQL腳本的OVER()子句中,使用Partition By 子句進行分區。在同一分區中,可以按照一定的條件,把分區中的數據行再次細分,按照固定數量(rows)或值范圍(range)來限制數據行,把這個數據行集稱作一個窗口。

通常把分區和窗口不加區分,統稱為窗口。在下文中,為了便於區分,我們把Partition By作用的結果記作分區,把rows 和 range作用的結果記作窗口,窗口中的數據行是分區的子集。

窗口函數是應用於窗口和分區的函數,共分為三類:排名函數,分析函數和聚合函數。注意OVER()子句執行的順序:OVER()子句在SELECT子句和DISTINCT子句之后執行,在ORDER BY子句之前執行,DISTINCT子句是在SELECT子句之后執行。

下文使用的示例數據使用以下代碼創建:

create table dbo.dt_test
(
ID int,
Code int
)
go

--insert data
insert into dbo.dt_test(ID,Code)
values(3,1),(3,2),(1,1),(1,2),(2,3),(1,2)
go
View Code

一,基於分區的運算

窗口和分區都是通過OVER()子句來定義的,使用partition by 子句把結果集划分為多個分區,使用rows 或 range命令把分區分為多個窗口。

基於分區的運算主要是指:

  • 基於分區做聚合運算時:把每個分區作為一個GROUP BY的分組,基於分組做聚合運算。
  • 基於分區做排名運算時:分區中的行集按照OVER()子句的ORDER BY子句指定的順序排名。
  • 基於分區做數據分析:主要是基於分區做百分位、相對位置百分比、排名百分比等運算。

當使用Over()函數計算整個分區的聚合值時,partition by子句是必需的,Order by 子句要省略:

select ID
    ,Code
    ,count(0) over(partition by Code) as Count_Over
    ,sum(ID) over(partition by Code) as Sum_Over
from dbo.dt_test

二,限制窗口的數據行

OVER()子句使用Rows和Range來限制分區中的數據行,用於對分區的數據行進行細分,得到分區的部分數據行。這兩個兩個關鍵字必須跟在Order By子句,都是基於當前行(Current Row)向前或向后來限制分區的數據行。

OVER ( [ <PARTITION BY clause> ]  [ <ORDER BY clause> ]  [ <ROW or RANGE clause> ] ) 

<ROW or RANGE clause> ::= { ROWS | RANGE } <window frame between>

<window frame between> ::= BETWEEN <window frame preceding> AND <window frame following>
<window frame preceding> ::= UNBOUNDED PRECEDING  | unsigned_value PRECEDING  | CURRENT ROW
<window frame following> ::= UNBOUNDED FOLLOWING  | unsigned_value FOLLOWING  | CURRENT ROW

Rows 和 Range子句中的特殊關鍵字:

  • UNBOUNDED PRECEDING:用於指定分區的第一行
  • UNBOUNDED FOLLOWING:用於指定分區的最后一行
  • CURRENT ROW:指定當前數據行
  • <unsigned_value> PRECEDING:在分區中,指定相對於當前行之前的數據行數量,unsigned_value是>0的整數
  • <unsigned_value> FOLLOWING:在分區中,指定相對於當前行之后的數據行數量,unsigned_value是>0的整數

在Over()子句中,使用Rows 或Range 命令進一步限制分區的數據行,在對分區進行細分時,必須注意:

1,必需條件

Rows 和 Range必須跟在Order by 子句之后,在同一個分區中對排序的結果集進行限制。經過我的觀測,當在分區中使用order by子句后,SQL Server 默認的操作是:在order by子句之后追加 range between unbounded preceding and current row

range between unbounded preceding and current row

2,Rows 關鍵字

在同一個分區中,基於當前行(Current Row),通過指定一個固定數量的行數來限制分區中的數據行,

例子1,在同一個分區中,從分區的第一行到當前行:

rows between unbounded preceding and current row

例子2,同一分區中,從當前行到下一行:

rows between current row and 1 following

3,Range 關鍵字

在同一分區中,使用排序列的值的范圍來限制分區中的數據行,當排序列存在重復值,重復的多行屬於同一個范圍。Range命令只能用於從分區的開始或者從結尾到當前行,不能使用 <unsigned_value> PRECEDING 和<unsigned_value>  FOLLOWING,Range命令的格式只能是:

range between unbounded preceding and current row
range between current row and unbounded following

4,Rows 和 Range的區別

在同一個分區中,對於下面的命令,如果排序列不存在重復值,那么Rows和Range返回的結果是相同的;如果排序行存儲在重復值,那么Rows和Range返回的結果可能不同。

range|rows between unbounded preceding and current row
range|rows between current row and unbounded following

三,舉例說明基於值范圍(Range)的滑動窗口

窗口可以基於值范圍進行滑動,從窗口的第一行開始,到當前行的值結束。在Over()子句中,Partition By定義分區,Order By定義窗口中滑動的方向。

舉個例子,使用以下代碼創建基於值范圍的滑動窗口:

select ID
    ,Code
    ,count(0) over(partition by Code order by ID) as Count_Over
    ,sum(ID) over(partition by Code order by ID) as Sum_Over
    ,count(0) over(partition by Code order by ID range between unbounded preceding and current row) as Count_Over
    ,sum(ID) over(partition by Code order by ID range between unbounded preceding and current row) as Sum_Over
from dbo.dt_test
order by Code
  ,ID

查詢的結果按照 Code 和ID 排序,在Over()子句中,按照Code分區,按照ID排序。

分析查詢結果

1,當Code=1時

當Code=1時,在這個分區(窗口)中,有兩行數據,ID分別是1,3

當Code=1,ID=1時,是分區的第一行,Count_Over=1,Sum_Over=1,這是滑動窗口的第一行,聚合值是的計算邏輯是

select count(0) as Count_Over,
    sum(ID) as Sum_Over
from dbo.dt_test
where Code=1 
 and ID<=1

當Code=1,ID=3時,是分區的第二行,Count_Over=2,Sum_Over=4,這是滑動窗口的第二行,聚合值是的計算邏輯是

select count(0) as Count_Over, sum(ID) as Sum_Over from dbo.dt_test where Code=1  and ID<=3

2,當Code=2時

當Code=2時,在這個分區中,有三種,ID分別是1,1,3

當Code=2,ID=1時,是分區的第一行,Count_Over=2,Sum_Over=2,這是滑動窗口的第一行,聚合值是的計算邏輯是

select count(0) as Count_Over, sum(ID) as Sum_Over from dbo.dt_test where Code=2 and ID<=1

當Code=2,ID=1時,是分區的第二行,Count_Over=2,Sum_Over=2,這是滑動窗口的第二行,聚合值是的計算邏輯是

select count(0) as Count_Over, sum(ID) as Sum_Over from dbo.dt_test where Code=2 and ID<=1

當Code=2,ID=3時,是分區的第三行,Count_Over=3,Sum_Over=5,這是滑動窗口的第三行,聚合值是的計算邏輯是

select count(0) as Count_Over, sum(ID) as Sum_Over from dbo.dt_test where Code=2 and ID<=3

四,基於滑動窗口的運算舉例

例子1,對分區中的連續兩行計算加和

計算邏輯是:在當前分區中,對當前行和其之后的1行數據計算加和;

select ID,Code,sum(code) over(partition by ID order by Code rows between current row and 1 following) as SumCode
from dbo.dt_test

例子2,對分區中的數據行,按照特定順序,計算從第一行到當前行的累加值

計算邏輯是:從第一行到當前行,計算累加值

select ID,Code,
    sum(code) over(partition by ID order by Code rows between unbounded preceding and current row) as SumCode_Rows,
    sum(code) over(partition by ID order by Code range between unbounded preceding and current row) as SumCode_Range
from dbo.dt_test

 

參考文檔:

OVER Clause (Transact-SQL)

SQL Server Window Function 窗體函數讀書筆記二 - A Detailed Look at Window Functions

總結SQL Server窗口函數的簡單使用

SQL Server中的窗口函數


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM