Die SQL OVER () -Klausel - wann und warum ist sie nützlich?

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

Ich habe über diese Klausel gelesen und verstehe nicht, warum ich sie brauche. Was macht die Funktion Over? Was macht Partitioning Bydas Warum kann ich beim Schreiben keine Anfrage stellen Group By SalesOrderID?

Mit fliegenden Farben
quelle
30
Unabhängig davon, welches RDBMS Sie verwenden, kann das Postgres-Lernprogramm hilfreich sein. Hat Beispiele; half mir.
Andrew Lazarus

Antworten:

144

Sie können verwenden GROUP BY SalesOrderID. Der Unterschied besteht darin, dass Sie mit GROUP BY nur die aggregierten Werte für die Spalten haben können, die nicht in GROUP BY enthalten sind.

Im Gegensatz dazu können Sie mithilfe von Fensteraggregatfunktionen anstelle von GROUP BY sowohl aggregierte als auch nicht aggregierte Werte abrufen. Das heißt, obwohl Sie dies in Ihrer Beispielabfrage nicht tun, können Sie sowohl einzelne OrderQtyWerte als auch deren Summen, Zählungen, Durchschnittswerte usw. über Gruppen derselben SalesOrderIDs abrufen .

Hier ist ein praktisches Beispiel dafür, warum Fensteraggregate großartig sind. Angenommen, Sie müssen berechnen, wie viel Prozent eines Gesamtwerts jeder Wert ist. Ohne Fensteraggregate müssten Sie zuerst eine Liste der aggregierten Werte ableiten und diese dann wieder mit dem ursprünglichen Rowset verbinden, dh wie folgt:

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]

Schauen Sie sich nun an, wie Sie dasselbe mit einem Fensteraggregat tun können:

SELECT
  [Partition],
  Value,
  Value * 100.0 / SUM(Value) OVER (PARTITION BY [Partition]) AS ValuePercent
FROM OriginalRowset orig

Viel einfacher und sauberer, nicht wahr?

Andriy M.
quelle
68

Die OVERKlausel ist insofern mächtig, als Sie Aggregate über verschiedene Bereiche ("Fenster") haben können, unabhängig davon, ob Sie a verwenden GROUP BYoder nicht

Beispiel: Anzahl pro SalesOrderIDund Anzahl von allen abrufen

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

Holen Sie sich verschiedene COUNTs, neinGROUP 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)
gbn
quelle
47

Wenn Sie nur nach der SalesOrderID gruppieren möchten, können Sie die Spalten ProductID und OrderQty nicht in die SELECT-Klausel aufnehmen.

Mit der PARTITION BY-Klausel können Sie Ihre Aggregatfunktionen aufteilen. Ein naheliegendes und nützliches Beispiel wäre, wenn Sie Zeilennummern für Bestellpositionen in einer Bestellung generieren möchten:

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

(Meine Syntax ist möglicherweise etwas falsch)

Sie würden dann etwas zurückbekommen wie:

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
Tom H.
quelle
42

Lassen Sie mich anhand eines Beispiels erklären, und Sie können sehen, wie es funktioniert.

Angenommen, Sie haben die folgende Tabelle DIM_EQUIPMENT:

VIN         MAKE    MODEL   YEAR    COLOR
-----------------------------------------
1234ASDF    Ford    Taurus  2008    White
1234JKLM    Chevy   Truck   2005    Green
5678ASDF    Ford    Mustang 2008    Yellow

Führen Sie unter SQL aus

SELECT VIN,
  MAKE,
  MODEL,
  YEAR,
  COLOR ,
  COUNT(*) OVER (PARTITION BY YEAR) AS COUNT2
FROM DIM_EQUIPMENT

Das Ergebnis wäre wie folgt

VIN         MAKE    MODEL   YEAR    COLOR     COUNT2
 ----------------------------------------------  
1234JKLM    Chevy   Truck   2005    Green     1
5678ASDF    Ford    Mustang 2008    Yellow    2
1234ASDF    Ford    Taurus  2008    White     2

Sehen Sie, was passiert ist.

Sie können ohne Gruppierung nach JAHR zählen und mit REIHE abgleichen.

Ein weiterer interessanter Weg, um das gleiche Ergebnis zu erzielen, wenn WITH wie unten unter Verwendung der WITH-Klausel als Inline-VIEW funktioniert und die Abfrage besonders komplexer vereinfachen kann, was hier jedoch nicht der Fall ist, da ich nur versuche, die Verwendung zu zeigen

 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;
Sanjay Singh
quelle
17

Die OVER-Klausel in Kombination mit PARTITION BY besagt, dass der vorhergehende Funktionsaufruf analytisch durchgeführt werden muss, indem die zurückgegebenen Zeilen der Abfrage ausgewertet werden. Stellen Sie sich das als Inline-Anweisung GROUP BY vor.

OVER (PARTITION BY SalesOrderID) gibt an, dass für die Funktion SUM, AVG usw. der Wert OVER eine Teilmenge der zurückgegebenen Datensätze aus der Abfrage zurückgegeben und diese Teilmenge durch den Fremdschlüssel SalesOrderID PARTITIONIERT wird.

Daher summieren wir jeden OrderQty-Datensatz für JEDE EINZIGARTIGE SalesOrderID, und dieser Spaltenname heißt 'Total'.

Es ist ein VIEL effizienteres Mittel als die Verwendung mehrerer Inline-Ansichten, um dieselben Informationen herauszufinden. Sie können diese Abfrage in eine Inline-Ansicht einfügen und dann nach Gesamt filtern.

SELECT ...,
FROM (your query) inlineview
WHERE Total < 200
maple_shaft
quelle
2
  • Auch Query PetitionKlausel genannt.
  • Ähnlich der Group ByKlausel

    • Daten in Blöcke (oder Partitionen) aufteilen
    • durch Partitionsgrenzen getrennt
    • Funktion wird innerhalb von Partitionen ausgeführt
    • Beim Überschreiten der Trenngrenze neu initialisiert

Syntax:
function (...) OVER (PARTITION BY col1 col3, ...)

  • Funktionen

    • Vertrauen Funktionen wie COUNT(), SUM(), MIN(), MAX(), usw.
    • Neue Funktionen wie auch (zB ROW_NUMBER(), RATION_TO_REOIRT()usw.)


Weitere Informationen mit Beispiel: http://msdn.microsoft.com/en-us/library/ms189461.aspx

Elshan
quelle
-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

Das ist ein Ergebnis der Abfrage. Die als Quelle verwendete Tabelle ist dieselbe, außer dass sie keine letzte Spalte enthält. Diese Spalte ist eine gleitende Summe der dritten.

Abfrage:

SELECT prkey,whatsthat,cash,SUM(cash) over (order by whatsthat)
    FROM public.iuk order by whatsthat,prkey
    ;

(Tabelle geht als public.iuk)

sql version:  2012

Es ist etwas über dem Niveau von dbase (1986), ich weiß nicht, warum mehr als 25 Jahre benötigt wurden, um es fertigzustellen.

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