Twee vragen, één oplossing
De afgelopen weken kreeg ik van twee verschillende klanten een vrijwel identieke vraag. De ene klant wilde een uitbreiding in een bestaand programma om bij een bepaalde datum de eerstvolgende werkdag te bepalen. De andere wilde in een document een datum afdrukken waarop uiterlijk gereageerd kan worden. Voor deze datum geldt als regel dat men 20 dagen de tijd krijgt om te reageren. Als oplossing voor beide vragen heb ik een SQL functie gebruikt.
UDF
User Defined Functies zijn mogelijk vanaf SQL Server 2000. Tot nu toe heb ik hier echter maar sporadisch gebruik van gemaakt. Voor deze toepassing leek een functie me echter wel heel geschikt. Als je gaat rekenen met werkdagen zijn er echter wel een paar dingen waar je rekening mee moet houden.
Hoezo dag 1?
Met de SQL functie Datepart kun je oa. bepalen wat het dagnummer is van een bepaalde datum. Het eerste waar je dan tegenaan loopt, is dat de dagnummers per server kunnen verschillen. Soms is zondag dag 1, soms kiest men voor maandag. Ik heb dat opgelost door voor een datum waarvan ik weet dat het een maandag is, het dagnummer te bepalen. Ook kun je hiervoor de standaardfunctie @@Datefirst gebruiken.
Feestdagen
Verder is het handig om bij het bepalen van de feestdagen rekening te houden met de feestdagen. Eén van beide databases had al een tabel met feestdagen. In de andere database heb ik zo’n tabel toegevoegd. In principe volstaat hiervoor een tabel met twee kolommen: datum + omschrijving. Feestdagen die in het weekend vallen kun je buiten beschouwing laten.
Recursief
Wat deze functie doet is eerst het gewenste aantal werkdagen bij de startdatum optellen en vervolgens controleren of er in die periode nog een doordeweekse feestdag valt. Als dit zo is, wordt de functie opnieuw aangeroepen, net zo lang tot je het gewenste aantal dagen hebt bereikt.
CREATE FUNCTION [dbo].[BepaalDatumPlusAantalWerkdagen](@StartDatum datetime, @Werkdagen int) returns datetime AS BEGIN DECLARE @BeginVanDeWeek datetime, @DagenVoor int, @EindDatum datetime, @Feestdagen int, -- maandag kan dag 1 of dag 2 zijn: dit is serverafhankelijk @maandag int, @vrijdag int -- 28-12-2009 valt op een maandag SET @maandag = Datepart(weekday,'20091228') SET @vrijdag = @maandag + 4 SET @DagenVoor = (DATEPART(dw,@StartDatum) + @@datefirst - 2)%7 -- bepaal de maandag van de startweek SET @BeginVanDeWeek = @StartDatum - @DagenVoor SET @Werkdagen = @Werkdagen + CASE WHEN @DagenVoor kleiner_dan 5 THEN @DagenVoor ELSE 4 END SET @EindDatum = @BeginVanDeWeek + @Werkdagen/5*7 + @Werkdagen%5 SELECT @Feestdagen = count (*) FROM DBO.tblFeestdag WHERE Datum groter_dan @StartDatum AND Datum kleiner_dan = @EindDatum -- feestdag in weekend hier niet meetellen AND Datepart(weekday,datum) BETWEEN @maandag AND @vrijdag IF @Feestdagen groter_dan 0 BEGIN SET @Einddatum = DBO.BepaalDatumPlusAantalWerkdagen(@EindDatum, @Feestdagen) END RETURN @Einddatum END -- test -- SELECT Datum = DBO.BepaalDatumPlusAantalWerkdagen('20091225', 1)
PS: wanneer je bovenstaande code wilt gebruiken, vervang dan de woorden kleiner_dan en groter_dan door < en >:
En de klanten: hoe is het daarmee afgelopen? Doordat ik deze ene functie voor beide applicaties kan gebruiken, denk ik dat ik op tijd kan opleveren, zodat ik in ieder geval niet hoef te werken tijdens de feestdagen.

