Предложение SQL OVER () - когда и почему это полезно? - PullRequest
159 голосов
/ 02 июня 2011
    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?

Ответы [ 7 ]

136 голосов
/ 02 июня 2011

Вы можете использовать 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

Намного проще и чище, не правда ли?

66 голосов
/ 02 июня 2011

Предложение 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)
43 голосов
/ 02 июня 2011

Если вы хотите использовать только GROUP BY SalesOrderID, вы не сможете включить столбцы ProductID и OrderQty в предложение SELECT.

Предложение 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
38 голосов
/ 08 мая 2014

Позвольте мне объяснить на примере, и вы сможете увидеть, как это работает.

Предполагается, что у вас есть следующая таблица 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;
16 голосов
/ 02 июня 2011

Предложение OVER в сочетании с PARTITION BY заявляет, что предыдущий вызов функции должен выполняться аналитически путем оценки возвращаемых строк запроса. Думайте об этом как о встроенном выражении GROUP BY.

OVER (PARTITION BY SalesOrderID) заявляет, что для функции SUM, AVG и т. Д. ... возвращает значение OVER подмножество возвращенных записей из запроса и PARTITION, которое подмножество внешним ключом SalesOrderID.

Таким образом, мы будем СУММАТЬ каждую запись OrderQty для КАЖДОГО УНИКАЛЬНОГО SalesOrderID, и имя этого столбца будет называться «Всего».

Это НАМНОГО более эффективное средство, чем использование нескольких встроенных представлений для поиска одной и той же информации. Вы можете поместить этот запрос во встроенное представление и затем отфильтровать по итоговому значению.

SELECT ...,
FROM (your query) inlineview
WHERE Total < 200
2 голосов
/ 20 октября 2014
  • Также называется Query Petition Пункт.
  • Аналогично пункту Group By

    • разбить данные на куски (или разделы)
    • разделить границами разделов
    • функция выполняет внутри разделов
    • повторная инициализация при пересечении границы разделения

Синтаксис:
функция (...) OVER (PARTITION BY col1 col3, ...)

  • Функция

    • Знакомые функции, такие как COUNT(), SUM(), MIN(), MAX() и т. Д.
    • Новые функции (например, ROW_NUMBER(), RATION_TO_REOIRT() и т. Д.)


Больше информации с примером: http://msdn.microsoft.com/en-us/library/ms189461.aspx

0 голосов
/ 17 ноября 2016
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 лет.

...