La clausola SQL OVER (): quando e perché è utile?


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

Ho letto di quella clausola e non capisco perché ne ho bisogno. Cosa fa la funzione Over? Cosa fa Partitioning By? Perché non riesco a fare una query con la scrittura Group By SalesOrderID?


30
Non importa quale RDBMS usi, il tutorial di Postgres potrebbe essere utile. Ha esempi; mi ha aiutato.
Andrew Lazarus,

Risposte:


144

È possibile utilizzare GROUP BY SalesOrderID. La differenza è che con GROUP BY puoi avere solo i valori aggregati per le colonne che non sono inclusi in GROUP BY.

Al contrario, utilizzando le funzioni di aggregazione con finestre anziché GROUP BY, è possibile recuperare valori sia aggregati che non aggregati. Cioè, anche se non lo stai facendo nella tua query di esempio, potresti recuperare sia i singoli OrderQtyvalori che le loro somme, conteggi, medie ecc. Su gruppi degli stessi SalesOrderID.

Ecco un esempio pratico del perché gli aggregati con finestre sono fantastici. Supponiamo di dover calcolare quale percentuale del totale è ogni valore. Senza aggregati con finestre dovresti prima derivare un elenco di valori aggregati e poi ricollegarlo al set di righe originale, ovvero in questo modo:

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]

Ora guarda come puoi fare lo stesso con un aggregato con finestre:

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

Molto più facile e pulito, no?


68

La OVERclausola è potente in quanto è possibile avere aggregati su intervalli diversi ("windowing"), sia che si usi GROUP BYo meno

Esempio: ottenere il conteggio per SalesOrderIDe il conteggio di tutti

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

Ottieni diverse COUNTs, noGROUP 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

Se si desidera solo Raggruppare per SalesOrderID, non sarà possibile includere le colonne ProductID e OrderQty nella clausola SELECT.

La clausola PARTITION BY consente di suddividere le funzioni aggregate. Un esempio ovvio e utile sarebbe se volessi generare numeri di riga per le righe ordine su un ordine:

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

(La mia sintassi potrebbe essere leggermente disattivata)

Ritorneresti quindi a qualcosa del tipo:

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

Lasciami spiegare con un esempio e potresti vedere come funziona.

Supponendo di avere la seguente tabella DIM_EQUIPMENT:

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

Esegui sotto SQL

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

Il risultato sarebbe come di seguito

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

Guarda cosa è successo.

Puoi contare senza Raggruppa per ANNO e Match con ROW.

Un altro MODO interessante per ottenere lo stesso risultato se come di seguito usando la clausola WITH, WITH funziona come VISUALIZZA IN LINEA e può semplificare la query soprattutto quelle complesse, il che non è il caso qui poiché sto solo cercando di mostrare l'utilizzo

 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

La clausola OVER quando combinata con PARTITION BY indica che la precedente chiamata di funzione deve essere eseguita analiticamente valutando le righe restituite della query. Pensalo come un'affermazione GROUP BY in linea.

OVER (PARTITION BY SalesOrderID) indica che per la funzione SOMMA, AVG, ecc ..., restituisce il valore OVER un sottoinsieme dei record restituiti dalla query e PARTITION che sottoinsieme della chiave esterna SalesOrderID.

Quindi sommeremo tutti i record OrderQty per OGNI UNIQUE SalesOrderID e il nome di quella colonna verrà chiamato "Totale".

È un modo MOLTO più efficiente rispetto all'utilizzo di più viste in linea per scoprire le stesse informazioni. Quindi puoi inserire questa query in una vista incorporata e filtrare su Totale.

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

2
  • Chiamata anche Query Petitionclausola.
  • Simile alla Group Byclausola

    • suddividere i dati in blocchi (o partizioni)
    • separato da limiti di partizione
    • la funzione svolge all'interno di partizioni
    • reinizializzato quando si attraversa il confine di divisione

Sintassi:
funzione (...) OVER (PARTITION BY col1 col3, ...)

  • funzioni

    • Funzioni familiari come COUNT(), SUM(), MIN(), MAX(), ecc
    • Anche nuove funzioni (ad es ROW_NUMBER(). RATION_TO_REOIRT(), Ecc.)


Ulteriori informazioni con l'esempio: 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

Questo è il risultato della query. La tabella usata come sorgente è lo stesso exept che non ha l'ultima colonna. Questa colonna è una somma mobile della terza.

Query:

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

(la tabella va come public.iuk)

sql version:  2012

È un po 'oltre il livello di database (1986), non so perché siano stati necessari oltre 25 anni per finirlo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.