SQL OVER()子句 – 何时以及为何有用?

USE AdventureWorks2008R2; 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); 

我读了关于这个条款,我不明白为什么我需要它。 这个functionOver做什么? Partitioning By做什么的? 为什么我无法通过编写Group By SalesOrderID进行查询?

可以使用GROUP BY SalesOrderID 。 与GROUP BY不同的是,您只能拥有不包含在GROUP BY中的列的汇总值。

相比之下,使用窗口集合函数而不是GROUP BY,可以检索聚合值和非聚合值。 也就是说,虽然在示例查询中没有这样做,但您可以通过相同的SalesOrderID组检索单个OrderQty值及其总和,计数,平均值等。

这里有一个实际的例子,为什么窗口集合是伟大的。 假设你需要计算每个值总和的百分比。 没有窗口集合,你必须首先派生一个聚合值的列表,然后将它加回到原来的行集,即像这样:

 SELECT orig.[Partition], orig.Value, orig.Value * 100.0 / agg.TotalValue AS ValuePercent FROM OriginalRowset orig INNER JOIN ( SELECT [Partition], SUM(Value) AS TotalValue FROM OriginalRowset GROUP BY [Partition] ) agg ON orig.[Partition] = agg.[Partition] 

现在看看如何用窗口集合来做同样的事情:

 SELECT [Partition], Value, Value * 100.0 / SUM(Value) OVER (PARTITION BY [Partition]) AS ValuePercent FROM OriginalRowset orig 

更容易和更清洁,不是吗?

OVER子句是强大的,因为您可以在不同范围(“窗口”)上进行聚合,无论您是否使用GROUP BY

示例:按SalesOrderID获取计数并计数所有

 SELECT SalesOrderID, ProductID, OrderQty ,COUNT(OrderQty) AS 'Count' ,COUNT(*) OVER () AS 'CountAll' FROM Sales.SalesOrderDetail WHERE SalesOrderID IN(43659,43664) GROUP BY SalesOrderID, ProductID, OrderQty 

获取不同的COUNT ,没有GROUP BY

 SELECT SalesOrderID, ProductID, OrderQty ,COUNT(OrderQty) OVER(PARTITION BY SalesOrderID) AS 'CountQtyPerOrder' ,COUNT(OrderQty) OVER(PARTITION BY ProductID) AS 'CountQtyPerProduct', ,COUNT(*) OVER () AS 'CountAllAgain' FROM Sales.SalesOrderDetail WHERE SalesOrderID IN(43659,43664) 

如果您只想要GROUP BY SalesOrderID,那么您将无法在SELECT子句中包含ProductID和OrderQty列。

PARTITION BY子句让你分解你的聚合函数。 一个显而易见的,有用的例子是,如果你想为订单上的订单行生成行号:

 SELECT O.order_id, O.order_date, ROW_NUMBER() OVER(PARTITION BY O.order_id) AS line_item_no, OL.product_id FROM Orders O INNER JOIN Order_Lines OL ON OL.order_id = O.order_id 

(我的语法可能会略微closures)

然后你会得到像这样的东西:

 order_id order_date line_item_no product_id -------- ---------- ------------ ---------- 1 2011-05-02 1 5 1 2011-05-02 2 4 1 2011-05-02 3 7 2 2011-05-12 1 8 2 2011-05-12 2 1 

让我用一个例子来解释,你将能够看到它是如何工作的。

假设您有以下DIM_EQUIPMENT表:

 VIN MAKE MODEL YEAR COLOR ----------------------------------------- 1234ASDF Ford Taurus 2008 White 1234JKLM Chevy Truck 2005 Green 5678ASDF Ford Mustang 2008 Yellow 

在SQL下运行

 SELECT VIN, MAKE, MODEL, YEAR, COLOR , COUNT(*) OVER (PARTITION BY YEAR) AS COUNT2 FROM DIM_EQUIPMENT 

结果如下

 VIN MAKE MODEL YEAR COLOR COUNT2 ---------------------------------------------- 1234JKLM Chevy Truck 2005 Green 1 5678ASDF Ford Mustang 2008 Yellow 2 1234ASDF Ford Taurus 2008 White 2 

看看发生了什么。

你可以在没有分组的情况下计数,并且与ROW匹配。

另一个有趣的方式来获得相同的结果,如果下面使用WITH子句,WITH作为行内VIEW,并可以简化查询特别复杂的,这是不是在这里虽然因为我只是试图显示用法

  WITH EQ AS ( SELECT YEAR AS YEAR2, COUNT(*) AS COUNT2 FROM DIM_EQUIPMENT GROUP BY YEAR ) SELECT VIN, MAKE, MODEL, YEAR, COLOR, COUNT2 FROM DIM_EQUIPMENT, EQ WHERE EQ.YEAR2=DIM_EQUIPMENT.YEAR; 

与PARTITION BY结合使用时,OVER子句声明前面的函数调用必须通过评估返回的查询行进行分析。 把它看作是一个内联的GROUP BY语句。

OVER (PARTITION BY SalesOrderID)指出,对于SUM,AVG等…函数,返回值OVER从查询返回的logging的子集和PARTITION子集由外键SalesOrderID。

因此,我们将为每个UNIQUE SalesOrderID累计每个OrderQtylogging,该列名称将被称为“总计”。

与使用多个内嵌视图来查找相同的信息相比,这是一个更有效的方法。 你可以把这个查询放在内联视图中,然后在Total上过滤。

 SELECT ..., FROM (your query) inlineview WHERE Total < 200 
  • 也称为Query Petition条款。
  • 类似于Group By子句

    • 将数据分解成块(或分区)
    • 由分区边界分开
    • 函数在分区内执行
    • 跨越分界线时重新初始化

句法:
函数(…)OVER(PARTITION BY col1 col3,…)

  • function

    • 熟悉的函数如COUNT()SUM()MIN()MAX()等等
    • 新函数(例如ROW_NUMBER()RATION_TO_REOIRT()等)

更多信息,例如: http : //msdn.microsoft.com/en-us/library/ms189461.aspx

 prkey whatsthat cash 890 "abb " 32 32 43 "abbz " 2 34 4 "bttu " 1 35 45 "gasstuff " 2 37 545 "gasz " 5 42 80009 "hoo " 9 51 2321 "ibm " 1 52 998 "krk " 2 54 42 "kx-5010 " 2 56 32 "lto " 4 60 543 "mp " 5 65 465 "multipower " 2 67 455 "ON " 1 68 7887 "prem " 7 75 434 "puma " 3 78 23 "retractble " 3 81 242 "Trujillo's stuff " 4 85 

这是查询的结果。 用作源的表是相同的exept,它没有最后一列。 这列是第三个移动的总和。

查询:

 SELECT prkey,whatsthat,cash,SUM(cash) over (order by whatsthat) FROM public.iuk order by whatsthat,prkey ; 

(表格为public.iuk)

 sql version: 2012 

这是一个有点超过dbase(1986)的水平,我不知道为什么需要25年以上才能完成它。