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

时间:2011-06-02 18:45:06

标签: sql sql-server aggregate-functions clause

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

我读到了这个条款,我不明白为什么需要它。 函数Over有什么作用? Partitioning By做了什么? 为什么我不能写Group By SalesOrderID来查询?

8 个答案:

答案 0 :(得分:134)

可以使用GROUP BY SalesOrderID。区别在于,对于GROUP BY,您只能拥有GROUP BY中未包含的列的聚合值。

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

这是一个实际的例子,说明为什么窗口聚合很好。假设您需要计算每个值的总百分比。如果没有窗口化聚合,您必须首先派生聚合值列表,然后将其连接回原始行集,即如下所示:

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

更简单,更清洁,不是吗?

答案 1 :(得分:65)

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)

答案 2 :(得分:43)

如果您只想要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

(我的语法可能略有偏差)

然后你会得到类似的东西:

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

答案 3 :(得分:37)

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

假设您有下表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

看看发生了什么。

你可以在没有Group By的情况下计算年份并与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;

答案 4 :(得分:16)

OVER子句与PARTITION BY结合使用时,必须通过评估返回的查询行来分析地完成前面的函数调用。可以将其视为内联GROUP BY语句。

OVER (PARTITION BY SalesOrderID)表示对于SUM,AVG等...函数,返回值OVER从查询中返回的记录的子集,以及PARTITION子集BY外键SalesOrderID。

因此,我们将为每个UNIQUE SalesOrderID的每个OrderQty记录进行SUM,并且该列名称将被称为“Total”。

与使用多个内联视图查找相同的信息相比,这是一种更有效的方法。您可以将此查询放在内联视图中,然后按总计过滤。

SELECT ...,
FROM (your query) inlineview
WHERE Total < 200

答案 5 :(得分:2)

  • 也称为Query Petition条款。
  • Group By条款类似

    • 将数据分解为块(或分区)
    • 由分区边界分隔
    • 功能在分区内执行
    • 在越过分界线时重新初始化

语法:
功能(...)OVER(分类为col1 col3,...)

  • 功能

    • 熟悉的功能,例如COUNT()SUM()MIN()MAX()
    • 新功能(例如ROW_NUMBER()RATION_TO_REOIRT()等)


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

答案 6 :(得分:0)

所以简单来说: Over 子句可用于选择非聚合值以及聚合值。

内部的

分区BY ORDER BY ROWS或RANGE 是OVER()by子句的一部分。

partition by用于对数据进行分区,然后执行这些窗口,聚合函数,如果没有分区,则将整个结果集视为单个分区。

OVER子句可与排名函数(Rank,Row_Number,Dense_Rank ..),聚合函数(如AVG,Max,Min,SUM等)和Analytics函数(如First_Value,Last_Value等)一起使用。

让我们看看OVER子句的基本语法

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

根据: 它用于对数据进行分区并在具有相同数据的组上执行操作。

订购者: 它用于定义分区中数据的逻辑顺序。如果不指定分区,则整个结果集将被视为单个分区

: 可以用来指定执行该操作时应该在分区中考虑哪些行。

让我们举个例子:

这是我的数据集:

Id          Name                                               Gender     Salary
----------- -------------------------------------------------- ---------- -----------
1           Mark                                               Male       5000
2           John                                               Male       4500
3           Pavan                                              Male       5000
4           Pam                                                Female     5500
5           Sara                                               Female     4000
6           Aradhya                                            Female     3500
7           Tom                                                Male       5500
8           Mary                                               Female     5000
9           Ben                                                Male       6500
10          Jodi                                               Female     7000
11          Tom                                                Male       5500
12          Ron                                                Male       5000

所以让我执行不同的场景,看看数据如何受到影响,我将从困难的语法变成简单的语法

Select *,SUM(salary) Over(order by salary RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as sum_sal from employees

Id          Name                                               Gender     Salary      sum_sal
----------- -------------------------------------------------- ---------- ----------- -----------
6           Aradhya                                            Female     3500        3500
5           Sara                                               Female     4000        7500
2           John                                               Male       4500        12000
3           Pavan                                              Male       5000        32000
1           Mark                                               Male       5000        32000
8           Mary                                               Female     5000        32000
12          Ron                                                Male       5000        32000
11          Tom                                                Male       5500        48500
7           Tom                                                Male       5500        48500
4           Pam                                                Female     5500        48500
9           Ben                                                Male       6500        55000
10          Jodi                                               Female     7000        62000

只需观察sum_sal部分。在这里,我使用的是按薪水排序,并使用“无限制的前导和当前行之间的范围” 。 在这种情况下,我们不使用分区,因此整个数据将被视为一个分区,并且我们按薪水排序。 这里重要的是未绑定的前行和当前行。这意味着当我们计算总和时,从每一行的开始行到当前行。 但是,如果我们看到薪水为5000且名称为“ Pavan”的行,则理想情况下应为17000,薪水为5000且名称为Mark的行应为22000。但是由于我们使用的是 RANGE 在这种情况下,如果找到任何相似的元素,则将它们视为相同的逻辑组并对它们执行操作,并为该组中的每个项目分配值。这就是为什么我们对薪金= 5000具有相同的值的原因。引擎将薪金提高到5000 = Name = Ron并计算总和,然后将其分配给所有薪金= 5000。

Select *,SUM(salary) Over(order by salary ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as sum_sal from employees


   Id          Name                                               Gender     Salary      sum_sal
----------- -------------------------------------------------- ---------- ----------- -----------
6           Aradhya                                            Female     3500        3500
5           Sara                                               Female     4000        7500
2           John                                               Male       4500        12000
3           Pavan                                              Male       5000        17000
1           Mark                                               Male       5000        22000
8           Mary                                               Female     5000        27000
12          Ron                                                Male       5000        32000
11          Tom                                                Male       5500        37500
7           Tom                                                Male       5500        43000
4           Pam                                                Female     5500        48500
9           Ben                                                Male       6500        55000
10          Jodi                                               Female     7000        62000

因此,对于无界前导行和当前行之间的行,区别在于相同值的项目而不是将它们分组在一起,它计算从开始行到当前行的SUM,并且不处理相同行的项目值与 RANGE

不同
Select *,SUM(salary) Over(order by salary) as sum_sal from employees

Id          Name                                               Gender     Salary      sum_sal
----------- -------------------------------------------------- ---------- ----------- -----------
6           Aradhya                                            Female     3500        3500
5           Sara                                               Female     4000        7500
2           John                                               Male       4500        12000
3           Pavan                                              Male       5000        32000
1           Mark                                               Male       5000        32000
8           Mary                                               Female     5000        32000
12          Ron                                                Male       5000        32000
11          Tom                                                Male       5500        48500
7           Tom                                                Male       5500        48500
4           Pam                                                Female     5500        48500
9           Ben                                                Male       6500        55000
10          Jodi                                               Female     7000        62000

这些结果与

相同
Select *, SUM(salary) Over(order by salary RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as sum_sal from employees

这是因为超额(按薪水排序)只是超额(按薪水排序)的无限制限制,当前行之间的距离 因此,无论我们在什么地方简单地指定排序依据而没有 ROWS或RANGE ,它都将未绑定先行和当前行之间的范围作为默认值。

注意:这仅适用于实际接受RANGE / ROW的函数。例如,ROW_NUMBER和其他一些人不接受RANGE / ROW,在这种情况下,这不会出现在图片中。

到目前为止,我们看到带有子句的Over子句采用了Range / ROWS,语法看起来像这样 UNBOUNDED PRECEDING和CURRENT ROW之间的范围 它实际上是从第一行开始计算到当前行。但是,如果要计算整个数据分区的值并为每一列(从第一行到最后一行)使用它,该怎么办。这是该查询

Select *,sum(salary) Over(order by salary ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) as sum_sal from employees

Id          Name                                               Gender     Salary      sum_sal
----------- -------------------------------------------------- ---------- ----------- -----------
1           Mark                                               Male       5000        62000
2           John                                               Male       4500        62000
3           Pavan                                              Male       5000        62000
4           Pam                                                Female     5500        62000
5           Sara                                               Female     4000        62000
6           Aradhya                                            Female     3500        62000
7           Tom                                                Male       5500        62000
8           Mary                                               Female     5000        62000
9           Ben                                                Male       6500        62000
10          Jodi                                               Female     7000        62000
11          Tom                                                Male       5500        62000
12          Ron                                                Male       5000        62000

我指定的是 UNBOUNDED FOLLOWING ,而不是CURRENT ROW,它指示引擎计算直到每一行的分区的最后一条记录。

现在您想知道什么是带有空花括号的OVER()?

这只是超额付款的捷径(按无约束的前提和无约束的跟随之间的工资行排序)

此处我们间接指定将所有结果集视为一个分区,然后从每个分区的第一条记录到最后一条记录进行计算。

Select *,Sum(salary) Over() as sum_sal from employees

Id          Name                                               Gender     Salary      sum_sal
----------- -------------------------------------------------- ---------- ----------- -----------
1           Mark                                               Male       5000        62000
2           John                                               Male       4500        62000
3           Pavan                                              Male       5000        62000
4           Pam                                                Female     5500        62000
5           Sara                                               Female     4000        62000
6           Aradhya                                            Female     3500        62000
7           Tom                                                Male       5500        62000
8           Mary                                               Female     5000        62000
9           Ben                                                Male       6500        62000
10          Jodi                                               Female     7000        62000
11          Tom                                                Male       5500        62000
12          Ron                                                Male       5000        62000

我确实为此制作了一个视频,如果您有兴趣可以访问它。 https://www.youtube.com/watch?v=CvVenuVUqto&t=1177s

谢谢, 帕万·库玛(Pavan Kumar Aryasomayajulu) HTTP://xyzcoder.github.io

答案 7 :(得分:-2)

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    "O.N.               "   1   68
7887   "prem               "   7   75
434    "puma               "   3   78
23     "retractble         "   3   81
242    "Trujillo's stuff   "   4   85

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

查询:

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年以上。