Предложение SQL OVER () - когда и почему это полезно?

169
    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?

WithFlyingColors
источник
30
Независимо от того, какую СУБД вы используете, руководство Postgres может быть полезным. Есть примеры; помог мне.
Андрей Лазарь

Ответы:

144

Вы можете использовать GROUP BY SalesOrderID. Разница в том, что с GROUP BY вы можете иметь только агрегированные значения для столбцов, которые не включены в GROUP BY.

Напротив, используя оконные агрегатные функции вместо GROUP BY, вы можете получать как агрегированные, так и неагрегированные значения. То есть, хотя вы не делаете этого в своем примере запроса, вы можете получить как отдельные OrderQtyзначения, так и их суммы, значения, средние значения и т. Д. По группам с одинаковыми значениями SalesOrderID.

Вот практический пример того, почему оконные агрегаты хороши. Предположим, вам нужно рассчитать, каков процент от общего значения. Без оконных агрегатов вам придется сначала получить список агрегированных значений, а затем присоединить его к исходному набору строк, например, так:

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

Намного проще и чище, не так ли?

Андрей М
источник
68

Предложение 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)
ГБН
источник
47

Если вы хотите использовать только 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
Том Х
источник
42

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

Предполагая, что у вас есть следующая таблица 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 на YEAR и совпадать с 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;
Санджай Сингх
источник
17

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

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

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

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

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

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

Синтаксис:
function (...) OVER (PARTITION BY col1 col3, ...)

  • функции

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


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

Эльшан
источник
-3
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 лет.

Алексей Неудачин
источник