Пункт 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?


30
Незалежно від того, якими RDBMS ви користуєтесь, підручник з 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

Отримати різні COUNTs, ні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

Якби ви хотіли згрупуватися за допомогою SalesOrderID, ви не зможете включити стовпці ProductID та OrderQty до пункту SELECT.

Пункт «ДІЛЕННЯ ДО» дозволить вам розбити свої сукупні функції. Одним очевидним і корисним прикладом може бути, якщо ви хочете генерувати номери рядків для рядків замовлення в порядку:

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, WH працює як рядковий перегляд і може спростити запит особливо складних, що тут не так, хоча я просто намагаюся показати використання

 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 для EACH UNIQUE SalesOrderID, і назва стовпця буде називатися "Total".

Це набагато ефективніший засіб, ніж використання декількох вбудованих поглядів, щоб дізнатися одну і ту ж інформацію. Ви можете розмістити цей запит у вбудованому вигляді та відфільтрувати на Всього.

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

2
  • Також називається Query Petition застереженням.
  • Подібно до Group 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+ років, щоб закінчити його.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.