Microsoft SQL Server
OVER clausule
Zoeken…
parameters
Parameter | Details |
---|---|
PARTITIE DOOR | De veld (en) die op PARTITION BY volgen, is het veld waarop de 'groepering' zal worden gebaseerd |
Opmerkingen
De clausule OVER bepaalt een venster of een subset van een rij binnen een set met queryresultaten. Een vensterfunctie kan worden toegepast om een waarde in te stellen en te berekenen voor elke rij in de set. De clausule OVER kan worden gebruikt met:
- Rangorde functies
- Geaggregeerde functies
zodat iemand geaggregeerde waarden kan berekenen, zoals voortschrijdende gemiddelden, cumulatieve aggregaten, lopende totalen of een top N per groepsresultaten.
Op een zeer abstracte manier kunnen we zeggen dat OVER zich gedraagt als GROEP DOOR. OVER wordt echter toegepast per veld / kolom en niet op de query als geheel zoals GROUP BY.
Opmerking # 1: In SQL Server 2008 (R2) ORDER BY Clausule kan niet worden gebruikt met geaggregeerde vensterfuncties ( link ).
Aggregatiefuncties gebruiken met OVER
Aan de hand van de tabel Auto's berekenen we het totale, maximale, minimale en gemiddelde geldbedrag dat elke klant heeft uitgegeven en we hebben meerdere keren (COUNT) meegenomen voor reparatie.
Id Klant-id MechanicId Model Status Totale kosten
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'
Houd er rekening mee dat het gebruik van OVER op deze manier de geretourneerde rijen niet verzamelt. De bovenstaande zoekopdracht retourneert het volgende:
Klanten ID | Totaal | Gem | tellen | min | Max |
---|---|---|---|---|---|
1 | 430 | 215 | 2 | 200 | 230 |
1 | 430 | 215 | 2 | 200 | 230 |
De dubbele rij (en) zijn mogelijk niet zo handig voor rapportagedoeleinden.
Als u gewoon gegevens wilt verzamelen, kunt u beter de GROUP BY-clausule gebruiken in combinatie met de juiste aggregatiefuncties, bijvoorbeeld:
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
Cumulatieve som
Met behulp van de artikelverkooptabel proberen we erachter te komen hoe de verkoop van onze artikelen stijgt door de datums. Hiertoe berekenen we de cumulatieve som van de totale omzet per artikelorder op de verkoopdatum.
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
Aggregatiefuncties gebruiken om de meest recente records te vinden
Met behulp van de bibliotheekdatabase proberen we voor elke auteur het laatste boek te vinden dat aan de database is toegevoegd. Voor dit eenvoudige voorbeeld gaan we uit van een altijd toenemende ID voor elke toegevoegde record.
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
In plaats van RANK kunnen twee andere functies worden gebruikt om te bestellen. In het vorige voorbeeld is het resultaat hetzelfde, maar ze geven verschillende resultaten wanneer de volgorde meerdere rijen voor elke rang geeft.
-
RANK()
: duplicaten krijgen dezelfde rang, de volgende rang houdt rekening met het aantal duplicaten in de vorige rang -
DENSE_RANK()
: duplicaten krijgen dezelfde rang, de volgende rang is altijd één hoger dan de vorige -
ROW_NUMBER()
: geeft elke rij een unieke 'rang', waarbij de duplicaten willekeurig worden 'geclassificeerd'
Als de tabel bijvoorbeeld een niet-unieke kolom CreationDate had en de bestelling op basis daarvan was gedaan, de volgende 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
Kan resulteren in:
Auteur | Titel | Aanmaakdatum | RANG | DENSE_RANK | RIJ NUMMER |
---|---|---|---|---|---|
Auteur 1 | Boek 1 | 22/07/2016 | 1 | 1 | 1 |
Auteur 1 | Boek 2 | 22/07/2016 | 1 | 1 | 2 |
Auteur 1 | Boek 3 | 21/07/2016 | 3 | 2 | 3 |
Auteur 1 | Boek 4 | 21/07/2016 | 3 | 2 | 4 |
Auteur 1 | Boek 5 | 21/07/2016 | 3 | 2 | 5 |
Auteur 1 | Boek 6 | 2016/04/07 | 6 | 3 | 6 |
Auteur 2 | Boek 7 | 2016/04/07 | 1 | 1 | 1 |
Gegevens verdelen in emmers met gelijke verdeling met behulp van NTILE
Laten we zeggen dat u examenscores hebt voor verschillende examens en u deze per examen in kwartielen wilt verdelen.
-- 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
werkt geweldig als je echt een bepaald aantal emmers nodig hebt en elk tot ongeveer hetzelfde niveau gevuld. Merk op dat het triviaal zou zijn om deze scores in percentielen te scheiden door eenvoudig ntile(100)
.