Microsoft SQL Server
Clausola OVER
Ricerca…
Parametri
Parametro | Dettagli |
---|---|
PARTITION BY | Il / i campo / i che segue PARTITION BY è quello su cui si baserà il 'raggruppamento' |
Osservazioni
La clausola OVER determina una finestra o un sottoinsieme di righe all'interno di una serie di risultati della query. È possibile applicare una funzione finestra per impostare e calcolare un valore per ogni riga nel set. La clausola OVER può essere utilizzata con:
- Funzioni di classificazione
- Funzioni aggregate
quindi qualcuno può calcolare valori aggregati come medie mobili, aggregati cumulativi, totali parziali o risultati N migliori per gruppo.
In un modo molto astratto possiamo dire che OVER si comporta come GROUP BY. Tuttavia, OVER viene applicato per campo / colonna e non per la query nel suo complesso come fa GROUP BY.
Nota n. 1: in SQL Server 2008 (R2), la clausola ORDER BY non può essere utilizzata con le funzioni della finestra di aggregazione ( collegamento ).
Utilizzo delle funzioni di aggregazione con OVER
Usando la tabella Cars , calcoleremo l'importo totale, massimo, minimo e medio di denaro speso da ogni cliente e molte volte (COUNT) ha portato una macchina per ripararla.
Id CustomerId MechanicId Status Status Costo totale
SELECT CustomerId,
SUM(TotalCost) OVER(PARTITION BY CustomerId) AS Total,
AVG(TotalCost) OVER(PARTITION BY CustomerId) AS Avg,
COUNT(TotalCost) OVER(PARTITION BY CustomerId) AS Count,
MIN(TotalCost) OVER(PARTITION BY CustomerId) AS Min,
MAX(TotalCost) OVER(PARTITION BY CustomerId) AS Max
FROM CarsTable
WHERE Status = 'READY'
Fai attenzione che l'utilizzo di OVER in questo modo non aggregherà le righe restituite. La query sopra riportata restituirà quanto segue:
Identificativo del cliente | Totale | Avg | Contare | min | Max |
---|---|---|---|---|---|
1 | 430 | 215 | 2 | 200 | 230 |
1 | 430 | 215 | 2 | 200 | 230 |
La / e riga / e duplicata / i potrebbe non essere quella utile ai fini della segnalazione.
Se si desidera semplicemente aggregare i dati, sarà meglio utilizzare la clausola GROUP BY insieme alle funzioni aggregate appropriate. Ad esempio:
SELECT CustomerId,
SUM(TotalCost) AS Total,
AVG(TotalCost) AS Avg,
COUNT(TotalCost) AS Count,
MIN(TotalCost) AS Min,
MAX(TotalCost) AS Max
FROM CarsTable
WHERE Status = 'READY'
GROUP BY CustomerId
Somma cumulativa
Usando la Tabella delle vendite degli articoli, cercheremo di scoprire come le vendite dei nostri articoli stanno aumentando attraverso le date. Per fare ciò calcoleremo la somma cumulativa delle vendite totali per ordine dell'articolo entro la data di vendita.
SELECT item_id, sale_Date
SUM(quantity * price) OVER(PARTITION BY item_id ORDER BY sale_Date ROWS BETWEEN UNBOUNDED PRECEDING) AS SalesTotal
FROM SalesTable
Utilizzo delle funzioni di aggregazione per trovare i record più recenti
Utilizzando il database delle biblioteche , cerchiamo di trovare l'ultimo libro aggiunto al database per ogni autore. Per questo semplice esempio, assumiamo un ID sempre crescente per ogni record aggiunto.
SELECT MostRecentBook.Name, MostRecentBook.Title
FROM ( SELECT Authors.Name,
Books.Title,
RANK() OVER (PARTITION BY Authors.Id ORDER BY Books.Id DESC) AS NewestRank
FROM Authors
JOIN Books ON Books.AuthorId = Authors.Id
) MostRecentBook
WHERE MostRecentBook.NewestRank = 1
Invece di RANK, è possibile utilizzare altre due funzioni per ordinare. Nell'esempio precedente il risultato sarà lo stesso, ma danno risultati diversi quando l'ordine dà più righe per ogni grado.
-
RANK()
: i duplicati ottengono lo stesso valore, il grado successivo prende in considerazione il numero di duplicati nel grado precedente -
DENSE_RANK()
: i duplicati ottengono lo stesso valore, il grado successivo è sempre superiore a quello precedente -
ROW_NUMBER()
: assegnerà a ciascuna riga un 'rank' unico, 'classifica' i duplicati casualmente
Ad esempio, se la tabella aveva una colonna non univoca CreationDate e l'ordine è stato eseguito in base a tale, la seguente query:
SELECT Authors.Name,
Books.Title,
Books.CreationDate,
RANK() OVER (PARTITION BY Authors.Id ORDER BY Books.CreationDate DESC) AS RANK,
DENSE_RANK() OVER (PARTITION BY Authors.Id ORDER BY Books.CreationDate DESC) AS DENSE_RANK,
ROW_NUMBER() OVER (PARTITION BY Authors.Id ORDER BY Books.CreationDate DESC) AS ROW_NUMBER,
FROM Authors
JOIN Books ON Books.AuthorId = Authors.Id
Potrebbe comportare:
Autore | Titolo | Data di creazione | RANGO | DENSE_RANK | ROW_NUMBER |
---|---|---|---|---|---|
Autore 1 | Prenota 1 | 22/07/2016 | 1 | 1 | 1 |
Autore 1 | Prenota 2 | 22/07/2016 | 1 | 1 | 2 |
Autore 1 | Prenota 3 | 21/07/2016 | 3 | 2 | 3 |
Autore 1 | Prenota 4 | 21/07/2016 | 3 | 2 | 4 |
Autore 1 | Prenota 5 | 21/07/2016 | 3 | 2 | 5 |
Autore 1 | Prenota 6 | 2016/04/07 | 6 | 3 | 6 |
Autore 2 | Prenota 7 | 2016/04/07 | 1 | 1 | 1 |
Dividere i dati in bucket ugualmente partizionati usando NTILE
Supponiamo che tu disponga di punteggi degli esami per diversi esami e che tu voglia dividerli in quartili per esame.
-- Setup data:
declare @values table(Id int identity(1,1) primary key, [Value] float, ExamId int)
insert into @values ([Value], ExamId) values
(65, 1), (40, 1), (99, 1), (100, 1), (90, 1), -- Exam 1 Scores
(91, 2), (88, 2), (83, 2), (91, 2), (78, 2), (67, 2), (77, 2) -- Exam 2 Scores
-- Separate into four buckets per exam:
select ExamId,
ntile(4) over (partition by ExamId order by [Value] desc) as Quartile,
Value, Id
from @values
order by ExamId, Quartile
ntile
funziona alla grande quando hai davvero bisogno di un certo numero di secchi e ognuno è riempito all'incirca allo stesso livello. Si noti che sarebbe banale separare questi punteggi in percentili semplicemente usando ntile(100)
.