Предложение 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);

Я читал об этой статье, и я не понимаю, зачем мне это нужно. Что делает функция Over? Что делает Partitioning By? Почему я не могу сделать запрос с записью Group By SalesOrderID?

Ответ 1

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

Гораздо проще и чище, не так ли?

Ответ 2

Предложение 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 s, no 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)

Ответ 3

Если вы хотите только 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

Ответ 4

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

Предполагая, что у вас есть следующая таблица 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 on YEAR и Match with ROW.

Еще один интересный способ получить тот же результат, если, как показано ниже, с помощью WITH clause, WITH работает как встроенный просмотр и может упростить запрос, особенно сложные, что здесь не так, поскольку я просто пытаюсь показать использование

 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;

Ответ 5

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

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

Итак, мы будем записывать каждую запись OrderQty для КАЖДОГО UNIQUE SalesOrderID, и это имя столбца будет называться "Total".

Это БОЛЬШЕ более эффективное средство, чем использование нескольких встроенных представлений, чтобы узнать ту же информацию. Вы можете поместить этот запрос в встроенное представление и затем фильтровать на Total.

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

Ответ 6

  • Также вызывается 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

Ответ 7

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 stuff   "   4   85

Это результат запроса. Таблица, используемая в качестве источника, такая же, как и в последнем столбце. Этот столбец является движущейся суммой третьего.

Query:

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 лет.