In dit artikel laat ik een manier zien om in SQL Server een voortschrijdend totaal over 4 weken te berekenen. Er leiden meer wegen naar Rome, maar dit is een manier die werkt met een datumdimensie.
Wat bedoel ik met een voortschrijdend totaal over 4 weken? Het is een som of gemiddelde, berekend over een aantal weken. Het getal schuift een week terug en pakt dan de volgende vier weken. In het voorbeeld hieronder nemen we week 37-40, dat noemen we blok 1. Dan gaan we een week terug in het verleden en nemen we week 36-39, dat noemen we blok 2. Enzovoort.

Waarom is dit handig? Dit voortschrijdende 4-wekentotaal kun je in een dashboard gebruiken voor een trendlijn of sparkline van belangrijke bedrijfsgegevens.
De eerste stap is bepalen hoeveel “groepen” van 4 weken data je nodig hebt. In mijn voorbeeld heb ik vier groepen weken, blok 1 - blok 4. Voor het SQL-voorbeeld gebruik ik zes blokken. De weken moeten in de tijd meeschuiven, zonder dat er iets hardcoded in staat, want het dashboard toont data tot en met de laatste volledige week.
Stap 1: Bepaal de laatste volledige week
DECLARE @EindWeek AS INT = (SELECT MIN(WeekNummer) FROM dbo.DimDatum WHERE DatumID = CONVERT(VARCHAR, DATEADD(dd,-7,GETDATE()),112) )Stap 2: Declareer blokken van weken
DECLARE @StartWeekBlok6 VARCHAR(6) -- (EindWeek -3 Weken) (dit is het laatste blok)DECLARE @EindWeekBlok6 VARCHAR(6)DECLARE @StartWeekBlok5 VARCHAR(6) -- (EindWeek -1 week) - 4 weken, of eigenlijk @EindWeek -4DECLARE @EindWeekBlok5 VARCHAR(6)DECLARE @StartWeekBlok4 VARCHAR(6) -- (EindWeek -2 weken) - 4 weken, of eigenlijk @EindWeek -5DECLARE @EindWeekBlok4 VARCHAR(6)DECLARE @StartWeekBlok3 VARCHAR(6) -- (EindWeek -3 weken) - 4 weken, of eigenlijk @EindWeek -6DECLARE @EindWeekBlok3 VARCHAR(6)DECLARE @StartWeekBlok2 VARCHAR(6) -- (EindWeek -4 weken) - 4 weken, of eigenlijk @EindWeek -7DECLARE @EindWeekBlok2 VARCHAR(6)DECLARE @StartWeekBlok1 VARCHAR(6) -- (EindWeek -5 weken) - 4 weken, of eigenlijk @EindWeek -8DECLARE @EindWeekBlok1 VARCHAR(6)Stap 2: Geef de blokken een waarde
SET @StartWeekBlok6 = (SELECT dbo.fnDateAddWeek(@EindWeek,-3,0))SET @StartWeekBlok5 = (SELECT dbo.fnDateAddWeek(@EindWeek,-4,0))SET @StartWeekBlok4 = (SELECT dbo.fnDateAddWeek(@EindWeek,-5,0))SET @StartWeekBlok3 = (SELECT dbo.fnDateAddWeek(@EindWeek,-6,0))SET @StartWeekBlok2 = (SELECT dbo.fnDateAddWeek(@EindWeek,-7,0))SET @StartWeekBlok1 = (SELECT dbo.fnDateAddWeek(@EindWeek,-8,0))
SET @EindWeekBlok6 = (SELECT dbo.fnDateAddWeek(@EindWeek,0,0))SET @EindWeekBlok5 = (SELECT dbo.fnDateAddWeek(@EindWeek,-1,0))SET @EindWeekBlok4 = (SELECT dbo.fnDateAddWeek(@EindWeek,-2,0))SET @EindWeekBlok3 = (SELECT dbo.fnDateAddWeek(@EindWeek,-3,0))SET @EindWeekBlok2 = (SELECT dbo.fnDateAddWeek(@EindWeek,-4,0))SET @EindWeekBlok1 = (SELECT dbo.fnDateAddWeek(@EindWeek,-5,0))** Let op: ik gebruik een functie om de weken te berekenen. Daarvoor heb je de datumdimensie nodig. Met deze functie haal je handig een week in het verleden of de toekomst op, op basis van een opgegeven week. Je kunt de startweek ook meetellen in het aantal weken.
CREATE FUNCTION [dbo].[fnDateAddWeek](@StartWeek INT,@NrWeeks INT,@IncludeStartWeek BIT=0 -- als je hier 1 zet telt hij een week minder terug of verder)-- [fnDateAddWeek]-- Berekent een weeknummer dat voor of na de opgegeven week ligt obv de opgegeven weken-- Bijv: het is nu week 50. Je wilt weten welke week 4 weken terug is, dit is 46-- SELECT dbo.fnDateAddWeek (201550, -4,0)-- SELECT dbo.fnDateAddWeek (201601, -4,0)
RETURNS INTASBEGINDECLARE @Result INT
SET @Result = (SELECT DD.WeekNummerFROM(SELECT CONVERT(VARCHAR, DATEADD(WK, @NrWeeks- (CASE WHEN @IncludeStartWeek=1 AND @NrWeeks < 0 THEN -1WHEN @IncludeStartWeek=1 AND @NrWeeks > 0 THEN 1 ELSE -0 END), D.DatumWaarde),112) AS DatumIDFROM dbo.DimDatum AS DWHERE DatumID = (SELECT MAX(DatumID) AS DatumIDFROM dbo.DimDatumWHERE WeekNummer = @StartWeek)) AS DJOIN DimDatum AS DD ON D.DatumID = DD.DatumID)RETURN @ResultENDStap 3: Maak een CTE met de measures om te groeperen
;WITH BSAS(SELECT DP.PeriodeNummer, DP.Jaar, RIGHT(DP.PeriodeNummer,2) AS PeriodeNaamKortGeenJaar, ISNULL(SUM(KPI.AantalPlaatsing),0) AS AantalPlaatsing, ISNULL(SUM(KPI.AantalVacatures),0) AS AantalVacatures, ISNULL(SUM(KPI.AantalSollicitaties),0) AS AantalSollicitatiesFROM ps.FactPeriodeKPI AS KPIJOIN dbo.DimPeriode AS DP ON KPI.PeriodeID = DP.PeriodeIDJOIN dbo.DimOrganisatie AS DO ON KPI.OrganisatieID = DO.OrganisatieIDJOIN dbo.DimBranch AS DB ON DO.BranchID = DB.BranchIDWHERE (@GeselecteerdeOrganisatieLevelID = 4OR (@GeselecteerdeOrganisatieLevelID = 3 AND DB.ZoneID = @GeselecteerdeOrganisatieID)OR (@GeselecteerdeOrganisatieLevelID = 2 AND DB.AreaID = @GeselecteerdeOrganisatieID)OR (@GeselecteerdeOrganisatieLevelID = 1 AND DB.BranchID = @GeselecteerdeOrganisatieID))AND DP.PeriodeGrootheidID = 1AND DP.PeriodeNummer BETWEEN @EindWeekBlok1 AND @EindWeekBlok6GROUP BY DP.PeriodeNummer,DP.Jaar
)Stap 4: Maak een CTE die de groepen genereert
, WG -- WG: WeekGroepjes: genereer een nummer voor groepjes van 4 weken, eindigt-- bij de laatste volledige week.AS(
SELECT 1 AS PeriodeNummerRolling4Weken, @StartWeekBlok6 AS StartWeek, @EindWeekBlok6 AS EindWeek, @StartWeekBlok6 + ' t/m ' + @EindWeekBlok6 AS SparklineLabelFROM dbo.DimDatum AS DD
UNION
SELECT 2 AS PeriodeNummerRolling4Weken, @StartWeekBlok5 AS StartWeek, @EindWeekBlok5 AS EindWeek, @StartWeekBlok5 + ' t/m ' + @EindWeekBlok5 AS SparklineLabel
UNION
SELECT 3 AS PeriodeNummerRolling4Weken, @StartWeekBlok4 AS StartWeek, @EindWeekBlok4 AS EindWeek, @StartWeekBlok4 + ' t/m ' + @EindWeekBlok4 AS SparklineLabel
UNION
SELECT 4 AS PeriodeNummerRolling4Weken, @StartWeekBlok3 AS StartWeek, @EindWeekBlok3 AS EindWeek, @StartWeekBlok3 + ' t/m ' + @EindWeekBlok3 AS SparklineLabel
UNION
SELECT 5 AS PeriodeNummerRolling2Weken, @StartWeekBlok2 AS StartWeek, @EindWeekBlok2 AS EindWeek, @StartWeekBlok2 + ' t/m ' + @EindWeekBlok2 AS SparklineLabel
UNION
SELECT 6 AS PeriodeNummerRolling1Weken, @StartWeekBlok1 AS StartWeek, @EindWeekBlok1 AS EindWeek, @StartWeekBlok1 + ' t/m ' + @EindWeekBlok1 AS SparklineLabel)Stap 5: Combineer beide CTE’s
SELECT WG.PeriodeNummerRolling4Weken, WG.StartWeek, WG.EindWeek, WG.SparklineLabel, SUM(BS.AantalPlaatsing) AS AantalPlaatsing, SUM(BS.AantalVacatures) AS AantalVacatures, SUM(BS.AantalSollicitaties) AS AantalSollicitatiesFROM WGJOIN BS ON BS.PeriodeNummer BETWEEN WG.StartWeek AND WG.EindWeekGROUP BY WG.PeriodeNummerRolling4Weken ,WG.StartWeek ,WG.EindWeek ,WG.SparklineLabelStap 6: het gecombineerde resultaat
DECLARE @EindWeek AS INT = (SELECT MIN(WeekNummer) FROM dbo.DimDatum WHERE DatumID = CONVERT(VARCHAR, DATEADD(dd,-7,GETDATE()),112) )
DECLARE @StartWeekBlok6 VARCHAR(6) -- (EindWeek -3 Weken) (dit is het laatste blok)DECLARE @EindWeekBlok6 VARCHAR(6)DECLARE @StartWeekBlok5 VARCHAR(6) -- (EindWeek -1 week) - 4 weken, of eigenlijk @EindWeek -4DECLARE @EindWeekBlok5 VARCHAR(6)DECLARE @StartWeekBlok4 VARCHAR(6) -- (EindWeek -2 weken) - 4 weken, of eigenlijk @EindWeek -5DECLARE @EindWeekBlok4 VARCHAR(6)DECLARE @StartWeekBlok3 VARCHAR(6) -- (EindWeek -3 weken) - 4 weken, of eigenlijk @EindWeek -6DECLARE @EindWeekBlok3 VARCHAR(6)DECLARE @StartWeekBlok2 VARCHAR(6) -- (EindWeek -4 weken) - 4 weken, of eigenlijk @EindWeek -7DECLARE @EindWeekBlok2 VARCHAR(6)DECLARE @StartWeekBlok1 VARCHAR(6) -- (EindWeek -5 weken) - 4 weken, of eigenlijk @EindWeek -8DECLARE @EindWeekBlok1 VARCHAR(6)
SET @StartWeekBlok6 = (SELECT dbo.fnDateAddWeek(@EindWeek,-3,0))SET @StartWeekBlok5 = (SELECT dbo.fnDateAddWeek(@EindWeek,-4,0))SET @StartWeekBlok4 = (SELECT dbo.fnDateAddWeek(@EindWeek,-5,0))SET @StartWeekBlok3 = (SELECT dbo.fnDateAddWeek(@EindWeek,-6,0))SET @StartWeekBlok2 = (SELECT dbo.fnDateAddWeek(@EindWeek,-7,0))SET @StartWeekBlok1 = (SELECT dbo.fnDateAddWeek(@EindWeek,-8,0))
SET @EindWeekBlok6 = (SELECT dbo.fnDateAddWeek(@EindWeek,0,0))SET @EindWeekBlok5 = (SELECT dbo.fnDateAddWeek(@EindWeek,-1,0))SET @EindWeekBlok4 = (SELECT dbo.fnDateAddWeek(@EindWeek,-2,0))SET @EindWeekBlok3 = (SELECT dbo.fnDateAddWeek(@EindWeek,-3,0))SET @EindWeekBlok2 = (SELECT dbo.fnDateAddWeek(@EindWeek,-4,0))SET @EindWeekBlok1 = (SELECT dbo.fnDateAddWeek(@EindWeek,-5,0))
--SELECT @StartWeekBlok6,@StartWeekBlok5,@StartWeekBlok4,@StartWeekBlok3,@StartWeekBlok2,@StartWeekBlok1--SELECT @EindWeekBlok6,@EindWeekBlok5,@EindWeekBlok4,@EindWeekBlok3,@EindWeekBlok2,@EindWeekBlok1
-- ** ------------------------------------------------------------------------------------------------------------- ** -----------------------------------------------------------------------------------------------------------;WITH BSAS(SELECT DP.PeriodeNummer, DP.Jaar, RIGHT(DP.PeriodeNummer,2) AS PeriodeNaamKortGeenJaar, ISNULL(SUM(KPI.AantalPlaatsing),0) AS AantalPlaatsing, ISNULL(SUM(KPI.AantalVacatures),0) AS AantalVacatures, ISNULL(SUM(KPI.AantalSollicitaties),0) AS AantalSollicitatiesFROM ps.FactPeriodeKPI AS KPIJOIN dbo.DimPeriode AS DP ON KPI.PeriodeID = DP.PeriodeIDJOIN dbo.DimOrganisatie AS DO ON KPI.OrganisatieID = DO.OrganisatieIDJOIN dbo.DimBranch AS DB ON DO.BranchID = DB.BranchIDWHERE (@GeselecteerdeOrganisatieLevelID = 4OR (@GeselecteerdeOrganisatieLevelID = 3 AND DB.ZoneID = @GeselecteerdeOrganisatieID)OR (@GeselecteerdeOrganisatieLevelID = 2 AND DB.AreaID = @GeselecteerdeOrganisatieID)OR (@GeselecteerdeOrganisatieLevelID = 1 AND DB.BranchID = @GeselecteerdeOrganisatieID))AND DP.PeriodeGrootheidID = 1AND DP.PeriodeNummer BETWEEN @EindWeekBlok1 AND @EindWeekBlok6GROUP BY DP.PeriodeNummer,DP.Jaar
)
, WG -- WG: WeekGroepjes: genereer een nummer voor groepjes van 4 weken, eindigt-- bij de laatste volledige week.AS(
SELECT 1 AS PeriodeNummerRolling4Weken, @StartWeekBlok6 AS StartWeek, @EindWeekBlok6 AS EindWeek, @StartWeekBlok6 + ' t/m ' + @EindWeekBlok6 AS SparklineLabelFROM dbo.DimDatum AS DD
UNION
SELECT 2 AS PeriodeNummerRolling4Weken, @StartWeekBlok5 AS StartWeek, @EindWeekBlok5 AS EindWeek, @StartWeekBlok5 + ' t/m ' + @EindWeekBlok5 AS SparklineLabel
UNION
SELECT 3 AS PeriodeNummerRolling4Weken, @StartWeekBlok4 AS StartWeek, @EindWeekBlok4 AS EindWeek, @StartWeekBlok4 + ' t/m ' + @EindWeekBlok4 AS SparklineLabel
UNION
SELECT 4 AS PeriodeNummerRolling4Weken, @StartWeekBlok3 AS StartWeek, @EindWeekBlok3 AS EindWeek, @StartWeekBlok3 + ' t/m ' + @EindWeekBlok3 AS SparklineLabel
UNION
SELECT 5 AS PeriodeNummerRolling2Weken, @StartWeekBlok2 AS StartWeek, @EindWeekBlok2 AS EindWeek, @StartWeekBlok2 + ' t/m ' + @EindWeekBlok2 AS SparklineLabel
UNION
SELECT 6 AS PeriodeNummerRolling1Weken, @StartWeekBlok1 AS StartWeek, @EindWeekBlok1 AS EindWeek, @StartWeekBlok1 + ' t/m ' + @EindWeekBlok1 AS SparklineLabel)
SELECT WG.PeriodeNummerRolling4Weken, WG.StartWeek, WG.EindWeek, WG.SparklineLabel, SUM(BS.AantalPlaatsing) AS AantalPlaatsing, SUM(BS.AantalVacatures) AS AantalVacatures, SUM(BS.AantalSollicitaties) AS AantalSollicitatiesFROM WGJOIN BS ON BS.PeriodeNummer BETWEEN WG.StartWeek AND WG.EindWeekGROUP BY WG.PeriodeNummerRolling4Weken ,WG.StartWeek ,WG.EindWeek ,WG.SparklineLabelJe kunt deze query als procedure opslaan en uitvoeren… Het resultaat ziet er ongeveer zo uit (dummydata):

Deze data gebruik ik voor mijn sparkline, die de trend van de laatste weken laat zien:

Veelgestelde vragen
Hoe bereken ik een voortschrijdend totaal over 4 weken in SQL Server?
Gebruik een datumdimensie. Zoek de laatste volledige week, declareer blokken van vier weken die elk een week terugschuiven en join die blokken aan een CTE met de measures. Groepeer daarna per blok.
Wat is een voortschrijdend totaal over 4 weken?
Het is een som of gemiddelde over een aantal weken. Het venster schuift steeds een week terug. Blok 1 neemt week 37-40, blok 2 neemt week 36-39, enzovoort.
Hoe vind ik de laatste volledige week in T-SQL?
Haal MIN(WeekNummer) op uit dbo.DimDatum voor de DatumID van vandaag min zeven dagen, omgezet met stijl 112. De weken schuiven dan vanzelf mee, zonder dat er iets hardcoded is.
Wat doet de functie fnDateAddWeek?
Hij geeft het weeknummer dat een opgegeven aantal weken voor of na een startweek ligt, op basis van de datumdimensie. Week 50 min vier weken geeft week 46. Je kunt de startweek ook meetellen.
Waarom een voortschrijdend totaal over 4 weken in een dashboard?
Het voedt een trendlijn of sparkline voor belangrijke bedrijfsgegevens. Het dashboard toont data tot en met de laatste volledige week, dus de weken moeten in de tijd meeschuiven zonder hardcoded waarden.
Reacties
Gerelateerde blogs
Doellijn/Stripline in Reporting Services
Running Totals / Cumulatieven berekenen
Parameter Security bij Export Excel