OVER 子句 (Transact-SQL)
在套用相關的視窗函數之前,決定資料列集的資料分割和排序。 也就是說,OVER 子句會定義查詢結果集內的視窗或使用者指定的資料列集。 然後視窗函數會針對視窗中的每個資料列來計算值。 您可以搭配函數使用 OVER 子句,以便計算彙總值,例如移動平均值、累計彙總、累加值或是每組前 N 個結果。
適用於:
適用於:SQL Server (SQL Server 2008 透過目前版本)、Windows Azure SQL 資料庫 (初始版本,透過目前版本)。 |
語法
OVER (
[ <PARTITION BY clause> ]
[ <ORDER BY clause> ]
[ <ROW or RANGE clause> ]
)
<PARTITION BY clause> ::=
PARTITION BY value_expression , ... [ n ]
<ORDER BY clause> ::=
ORDER BY order_by_expression
[ COLLATE collation_name ]
[ ASC | DESC ]
[ ,...n ]
<ROW or RANGE clause> ::=
{ ROWS | RANGE } <window frame extent>
<window frame extent> ::=
{ <window frame preceding>
| <window frame between>
}
<window frame between> ::=
BETWEEN <window frame bound> AND <window frame bound>
<window frame bound> ::=
{ <window frame preceding>
| <window frame following>
}
<window frame preceding> ::=
{
UNBOUNDED PRECEDING
| <unsigned_value_specification> PRECEDING
| CURRENT ROW
}
<window frame following> ::=
{
UNBOUNDED FOLLOWING
| <unsigned_value_specification> FOLLOWING
| CURRENT ROW
}
<unsigned value specification> ::=
{ <unsigned integer literal> }
引數
PARTITION BY
將查詢結果集分成幾個資料分割。 視窗函數會分別套用至每個資料分割,並且針對每個資料分割重新開始計算。value_expression
指定分割資料列集所根據的資料行。 value_expression 只能參考 FROM 子句所提供的資料行。 value_expression 無法參考選取清單中的運算式或別名。 value_expression 可以是資料行運算式、純量子查詢、純量函數或使用者定義的變數。<ORDER BY 子句>
定義結果集的每個資料分割內資料列的邏輯順序。 也就是說,它會指定執行視窗函數計算的邏輯順序。order_by_expression
指定排序的資料行或運算式。 order_by_expression 只能參考 FROM 子句所提供的資料行。 不能指定整數來代表資料行名稱或別名。COLLATE collation_name
指定應該根據 collation_name 中指定的定序來執行 ORDER BY 作業。 collation_name 可以是 Windows 定序名稱或 SQL 定序名稱。 如需詳細資訊,請參閱<定序與 Unicode 支援>。 COLLATE 只適用於 char、varchar、nchar 和 nvarchar 等類型的資料行。ASC | DESC
指定指定之資料行的值應該以遞增或遞減順序排序。 ASC 是預設排序次序。 Null 值會當做最低的可能值來處理。ROWS | RANGE
適用於:SQL Server 2012 至 SQL Server 2014。
指定資料分割內的起始點和結束點,以進一步限制資料分割中的資料列。 這可以藉由指定與目前資料列有關的資料列範圍 (透過邏輯關聯或實體關聯) 來完成。 可以使用 ROWS 子句來達成實體關聯。
ROWS 子句會限制資料分割內的資料列,方法是指定目前資料列之前或之後的固定資料列數。 另外,RANGE 子句會以邏輯方式限制資料分割內的資料列,方法是指定與目前資料列的值相關的值範圍。 前後的資料列是根據 ORDER BY 子句的順序定義。 視窗框架 “RANGE … CURRENT ROW …” 包括在 ORDER BY 運算式中與目前資料列相同值的所有資料列。 例如,ROWS BETWEEN 2 PRECEDING AND CURRENT ROW 表示此函數操作所在的資料列視窗大小為三個資料列,從之前的 2 個資料列直到目前的資料列。
注意
ROWS 或 RANGE 要求必須指定 ORDER BY 子句。如果 ORDER BY 包含多個順序運算式,則 CURRENT ROW FOR RANGE 會在判斷目前資料列時,考量 ORDER BY 清單中的所有資料列。
UNBOUNDED PRECEDING
適用於:SQL Server 2012 至 SQL Server 2014。
指定視窗從資料分割的第一個資料列開始。 只能將 UNBOUNDED PRECEDING 指定為視窗起點。
<指定不帶正負號的值> PRECEDING
與 <指定不帶正負號的值> 一起指定,指示要置於目前資料列前面的資料列或值的數目。 RANGE 不允許這項指定。CURRENT ROW
適用於:SQL Server 2012 至 SQL Server 2014。
指定在與 ROWS 一起使用時,視窗在目前的資料列開始或結束,或者在與 RANGE 一起使用時則為目前值。 CURRENT ROW 可以指定為開始點和結束點。
BETWEEN <繫結的視窗框架 > AND <繫結的視窗框架 >
適用於:SQL Server 2012 至 SQL Server 2014。
與 ROWS 或 RANGE 一起使用,以指定視窗的下 (開始) 邊界點和上 (結束) 邊界點。 <繫結的視窗框架> 會定義邊界開始點,而 <繫結的視窗框架> 會定義邊界結束點。 上限不能小於下限。
UNBOUNDED FOLLOWING
適用於:SQL Server 2012 至 SQL Server 2014。
指定視窗在資料分割的最後一個資料列結束。 只能將 UNBOUNDED FOLLOWING 指定為視窗結束點。 例如,RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING 會定義一個視窗,此視窗從資料分割的目前資料列開始,並結束於資料分割的最後一個資料列。
<指定不帶正負號的值> FOLLOWING
與 <指定不帶正負號的值> 一起指定,指示要置於目前資料列後面的資料列或值的數目。 將 <指定不帶正負號的值> FOLLOWING 指定為視窗開始點時,結束點必須是 <指定不帶正負號的值> FOLLOWING。 例如,ROWS BETWEEN 2 FOLLOWING AND 10 FOLLOWING 會定義一個視窗,此視窗從目前資料列後面的第二個資料列開始,並結束於目前資料列後面的第十個資料列。 RANGE 不允許這項指定。不帶正負號的整數常值
適用於:SQL Server 2012 至 SQL Server 2014。
一個正整數常值 (包括 0),可指定要置於目前資料列或值前面或後面的資料列或值的數目。 這項指定只對 ROWS 有效。
一般備註
在含有一個 FROM 子句的單一查詢中,可以使用一個以上的視窗函數。 每個函數的 OVER 子句在進行資料分割和進行排序時,都不一樣。
如未指定 PARTITION BY,此函數會將查詢結果集的所有資料列視為單一群組。
如果未指定 ORDER BY,則將整個資料分割用於視窗框架。 這只適用於不需要 ORDER BY 子句的函數。 如果未指定 ROWS/RANGE,但指定了 ORDER BY,則將 RANGE UNBOUNDED PRECEDING AND CURRENT ROW 當做視窗框架的預設值。 這只適用於可以接受選擇性 ROWS/RANGE 指定的函數。 例如,排名函數不能接受 ROWS/RANGE,因此,即使存在 ORDER BY 而不存在 ROWS/RANGE,這個視窗框架依然不適用。
如果指定了 ROWS/RANGE 而且 <之前的視窗框架> 用於 <視窗框架範圍> (簡短語法),則這個指定會用於視窗框架邊界開始點,而 CURRENT ROW 則用於邊界結束點。 例如,“ROWS 5 PRECEDING” 等於 “ROWS BETWEEN 5 PRECEDING AND CURRENT ROW”。
限制事項
OVER 子句不能搭配 CHECKSUM 彙總函式使用。
RANGE 不能搭配 <指定不帶正負號的值> PRECEDING 或 <指定不帶正負號的值> FOLLOWING 使用。
根據排名而定,可能不支援搭配 OVER 子句、<ORDER BY 子句> 及/或 <ROWS 和 RANGE 子句> 使用彙總或分析函數。
範例
A.搭配 ROW_NUMBER 函數來使用 OVER 子句
下列範例示範如何搭配 ROW_NUMBER 函數使用 OVER 子句,以針對資料分割內的每一個資料列顯示資料列號碼。 OVER 子句中指定的 ORDER BY 子句會依照 SalesYTD 資料行來排序每一個資料分割中的資料列。 SELECT 陳述式中的 ORDER BY 子句會決定傳回整個查詢結果集的順序。
USE AdventureWorks2012;
GO
SELECT ROW_NUMBER() OVER(PARTITION BY PostalCode ORDER BY SalesYTD DESC) AS "Row Number",
p.LastName, s.SalesYTD, a.PostalCode
FROM Sales.SalesPerson AS s
INNER JOIN Person.Person AS p
ON s.BusinessEntityID = p.BusinessEntityID
INNER JOIN Person.Address AS a
ON a.AddressID = p.BusinessEntityID
WHERE TerritoryID IS NOT NULL
AND SalesYTD <> 0
ORDER BY PostalCode;
GO
以下為結果集:
Row Number LastName SalesYTD PostalCode
--------------- ----------------------- --------------------- ----------
1 Mitchell 4251368.5497 98027
2 Blythe 3763178.1787 98027
3 Carson 3189418.3662 98027
4 Reiter 2315185.611 98027
5 Vargas 1453719.4653 98027
6 Ansman-Wolfe 1352577.1325 98027
1 Pak 4116871.2277 98055
2 Varkey Chudukatil 3121616.3202 98055
3 Saraiva 2604540.7172 98055
4 Ito 2458535.6169 98055
5 Valdez 1827066.7118 98055
6 Mensa-Annan 1576562.1966 98055
7 Campbell 1573012.9383 98055
8 Tsoflias 1421810.9242 98055
B.搭配彙總函式來使用 OVER 子句
下列範例會針對查詢傳回的所有資料列來搭配彙總函式使用 OVER 子句。 在這個範例中,使用 OVER 子句比使用子查詢來衍生彙總值更有效率。
USE AdventureWorks2012;
GO
SELECT SalesOrderID, ProductID, OrderQty
,SUM(OrderQty) OVER(PARTITION BY SalesOrderID) AS Total
,AVG(OrderQty) OVER(PARTITION BY SalesOrderID) AS "Avg"
,COUNT(OrderQty) OVER(PARTITION BY SalesOrderID) AS "Count"
,MIN(OrderQty) OVER(PARTITION BY SalesOrderID) AS "Min"
,MAX(OrderQty) OVER(PARTITION BY SalesOrderID) AS "Max"
FROM Sales.SalesOrderDetail
WHERE SalesOrderID IN(43659,43664);
GO
以下為結果集:
SalesOrderID ProductID OrderQty Total Avg Count Min Max
------------ ----------- -------- ----------- ----------- ----------- ------ ------
43659 776 1 26 2 12 1 6
43659 777 3 26 2 12 1 6
43659 778 1 26 2 12 1 6
43659 771 1 26 2 12 1 6
43659 772 1 26 2 12 1 6
43659 773 2 26 2 12 1 6
43659 774 1 26 2 12 1 6
43659 714 3 26 2 12 1 6
43659 716 1 26 2 12 1 6
43659 709 6 26 2 12 1 6
43659 712 2 26 2 12 1 6
43659 711 4 26 2 12 1 6
43664 772 1 14 1 8 1 4
43664 775 4 14 1 8 1 4
43664 714 1 14 1 8 1 4
43664 716 1 14 1 8 1 4
43664 777 2 14 1 8 1 4
43664 771 3 14 1 8 1 4
43664 773 1 14 1 8 1 4
43664 778 1 14 1 8 1 4
下列範例顯示在計算值中搭配彙總函式來使用 OVER 子句。
USE AdventureWorks2012;
GO
SELECT SalesOrderID, ProductID, OrderQty
,SUM(OrderQty) OVER(PARTITION BY SalesOrderID) AS Total
,CAST(1. * OrderQty / SUM(OrderQty) OVER(PARTITION BY SalesOrderID)
*100 AS DECIMAL(5,2))AS "Percent by ProductID"
FROM Sales.SalesOrderDetail
WHERE SalesOrderID IN(43659,43664);
GO
以下為結果集: 請注意,彙總是以 SalesOrderID 來計算,而且每個 SalesOrderID 的每一行都會計算出 Percent by ProductID。
SalesOrderID ProductID OrderQty Total Percent by ProductID
------------ ----------- -------- ----------- ---------------------------------------
43659 776 1 26 3.85
43659 777 3 26 11.54
43659 778 1 26 3.85
43659 771 1 26 3.85
43659 772 1 26 3.85
43659 773 2 26 7.69
43659 774 1 26 3.85
43659 714 3 26 11.54
43659 716 1 26 3.85
43659 709 6 26 23.08
43659 712 2 26 7.69
43659 711 4 26 15.38
43664 772 1 14 7.14
43664 775 4 14 28.57
43664 714 1 14 7.14
43664 716 1 14 7.14
43664 777 2 14 14.29
43664 771 3 14 21.4
43664 773 1 14 7.14
43664 778 1 14 7.14
(20 row(s) affected)
C.產生移動平均值和累計總和
下列範例搭配 OVER 子句來使用 AVG 與 SUM 函數,為 Sales.SalesPerson 資料表中各領域的年度銷售提供移動平均值和累計總和。 TerritoryID 負責分割資料,而 SalesYTD 會進行邏輯性地排序。 這表示,將會根據銷售年度來針對每一個領域計算 AVG 函數。 請注意,在 TerritoryID 1 中,2005 銷售年度有兩個資料列,分別表示在該年度有銷售業績的兩個銷售人員。 計算這兩個資料列的平均銷售額,然後將表示 2006 年度銷售額的第三個資料列納入計算。
USE AdventureWorks2012;
GO
SELECT BusinessEntityID, TerritoryID
,DATEPART(yy,ModifiedDate) AS SalesYear
,CONVERT(varchar(20),SalesYTD,1) AS SalesYTD
,CONVERT(varchar(20),AVG(SalesYTD) OVER (PARTITION BY TerritoryID
ORDER BY DATEPART(yy,ModifiedDate)
),1) AS MovingAvg
,CONVERT(varchar(20),SUM(SalesYTD) OVER (PARTITION BY TerritoryID
ORDER BY DATEPART(yy,ModifiedDate)
),1) AS CumulativeTotal
FROM Sales.SalesPerson
WHERE TerritoryID IS NULL OR TerritoryID < 5
ORDER BY TerritoryID,SalesYear;
以下為結果集:
BusinessEntityID TerritoryID SalesYear SalesYTD MovingAvg CumulativeTotal
---------------- ----------- ----------- -------------------- -------------------- --------------------
274 NULL 2005 559,697.56 559,697.56 559,697.56
287 NULL 2006 519,905.93 539,801.75 1,079,603.50
285 NULL 2007 172,524.45 417,375.98 1,252,127.95
283 1 2005 1,573,012.94 1,462,795.04 2,925,590.07
280 1 2005 1,352,577.13 1,462,795.04 2,925,590.07
284 1 2006 1,576,562.20 1,500,717.42 4,502,152.27
275 2 2005 3,763,178.18 3,763,178.18 3,763,178.18
277 3 2005 3,189,418.37 3,189,418.37 3,189,418.37
276 4 2005 4,251,368.55 3,354,952.08 6,709,904.17
281 4 2005 2,458,535.62 3,354,952.08 6,709,904.17
(10 row(s) affected)
在這個範例中,OVER 子句未包含 PARTITION BY。 這表示,該函數將套用到查詢所傳回的所有資料列。 OVER 子句中指定的 ORDER BY 子句會決定套用 AVG 函數的邏輯順序。 此查詢會依照 WHERE 子句中指定的所有銷售領域傳回依年度的銷售量移動平均值。 SELECT 陳述式中指定的 ORDER BY 子句會決定查詢的資料列顯示的順序。
SELECT BusinessEntityID, TerritoryID
,DATEPART(yy,ModifiedDate) AS SalesYear
,CONVERT(varchar(20),SalesYTD,1) AS SalesYTD
,CONVERT(varchar(20),AVG(SalesYTD) OVER (ORDER BY DATEPART(yy,ModifiedDate)
),1) AS MovingAvg
,CONVERT(varchar(20),SUM(SalesYTD) OVER (ORDER BY DATEPART(yy,ModifiedDate)
),1) AS CumulativeTotal
FROM Sales.SalesPerson
WHERE TerritoryID IS NULL OR TerritoryID < 5
ORDER BY SalesYear;
以下為結果集:
BusinessEntityID TerritoryID SalesYear SalesYTD MovingAvg CumulativeTotal
---------------- ----------- ----------- -------------------- -------------------- --------------------
274 NULL 2005 559,697.56 2,449,684.05 17,147,788.35
275 2 2005 3,763,178.18 2,449,684.05 17,147,788.35
276 4 2005 4,251,368.55 2,449,684.05 17,147,788.35
277 3 2005 3,189,418.37 2,449,684.05 17,147,788.35
280 1 2005 1,352,577.13 2,449,684.05 17,147,788.35
281 4 2005 2,458,535.62 2,449,684.05 17,147,788.35
283 1 2005 1,573,012.94 2,449,684.05 17,147,788.35
284 1 2006 1,576,562.20 2,138,250.72 19,244,256.47
287 NULL 2006 519,905.93 2,138,250.72 19,244,256.47
285 NULL 2007 172,524.45 1,941,678.09 19,416,780.93
(10 row(s) affected)
D.指定 ROWS 子句
適用於:SQL Server 2012 至 SQL Server 2014。 |
下列範例使用 ROWS 子句來定義一個視窗,其上的資料列會計算為目前資料列與隨後的資料列數目 N (此範例中為 1 個資料列)。
SELECT BusinessEntityID, TerritoryID
,CONVERT(varchar(20),SalesYTD,1) AS SalesYTD
,DATEPART(yy,ModifiedDate) AS SalesYear
,CONVERT(varchar(20),SUM(SalesYTD) OVER (PARTITION BY TerritoryID
ORDER BY DATEPART(yy,ModifiedDate)
ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING ),1) AS CumulativeTotal
FROM Sales.SalesPerson
WHERE TerritoryID IS NULL OR TerritoryID < 5;
以下為結果集:
BusinessEntityID TerritoryID SalesYTD SalesYear CumulativeTotal
---------------- ----------- -------------------- ----------- --------------------
274 NULL 559,697.56 2005 1,079,603.50
287 NULL 519,905.93 2006 692,430.38
285 NULL 172,524.45 2007 172,524.45
283 1 1,573,012.94 2005 2,925,590.07
280 1 1,352,577.13 2005 2,929,139.33
284 1 1,576,562.20 2006 1,576,562.20
275 2 3,763,178.18 2005 3,763,178.18
277 3 3,189,418.37 2005 3,189,418.37
276 4 4,251,368.55 2005 6,709,904.17
281 4 2,458,535.62 2005 2,458,535.62
在下列範例中,會使用 UNBOUNDED PRECEDING 來指定 ROWS 子句。 結果是視窗從資料分割的第一個資料列開始。
SELECT BusinessEntityID, TerritoryID
,CONVERT(varchar(20),SalesYTD,1) AS SalesYTD
,DATEPART(yy,ModifiedDate) AS SalesYear
,CONVERT(varchar(20),SUM(SalesYTD) OVER (PARTITION BY TerritoryID
ORDER BY DATEPART(yy,ModifiedDate)
ROWS UNBOUNDED PRECEDING),1) AS CumulativeTotal
FROM Sales.SalesPerson
WHERE TerritoryID IS NULL OR TerritoryID < 5;
以下為結果集:
BusinessEntityID TerritoryID SalesYTD SalesYear CumulativeTotal
---------------- ----------- -------------------- ----------- --------------------
274 NULL 559,697.56 2005 559,697.56
287 NULL 519,905.93 2006 1,079,603.50
285 NULL 172,524.45 2007 1,252,127.95
283 1 1,573,012.94 2005 1,573,012.94
280 1 1,352,577.13 2005 2,925,590.07
284 1 1,576,562.20 2006 4,502,152.27
275 2 3,763,178.18 2005 3,763,178.18
277 3 3,189,418.37 2005 3,189,418.37
276 4 4,251,368.55 2005 4,251,368.55
281 4 2,458,535.62 2005 6,709,904.17