This has been useful for our maintenance plan, as we can use one script and add code that will run on a specific day (like the last Sunday, or 1st Weekday), I have also used it for forecasting the times that Jobs will run on our servers.
The function only requires three inputs, the month you are interested in, the type, and the number.
It uses similar information as Microsoft use for monthly job schedules.
CREATE FUNCTION dbo.GetMonthDay(@Month DATETIME,@Type INT, @Counter INT) RETURNS DATETIME AS BEGINSET @Month = CONVERT(DATE,DATEADD(DAY,1-DATEPART(DAY,@Month),@Month))DECLARE @CurDate DATETIME=@Month,@Date DATETIME,@Matches INT=0,@TempDate DATETIMEWHILE @CurDateIF (SELECT (CASE @Type WHEN 1 THEN (CASE WHEN DATEPART(WeekDay,@CurDate)=1 THEN 1 ELSE 0 END)WHEN 2 THEN(CASE WHEN DATEPART(WeekDay,@CurDate)=2 THEN 1 ELSE 0 END)WHEN 3 THEN(CASE WHEN DATEPART(WeekDay,@CurDate)=3 THEN 1 ELSE 0 END)WHEN 4 THEN(CASE WHEN DATEPART(WeekDay,@CurDate)=4 THEN 1 ELSE 0 END)WHEN 5 THEN(CASE WHEN DATEPART(WeekDay,@CurDate)=5 THEN 1 ELSE 0 END)WHEN 6 THEN(CASE WHEN DATEPART(WeekDay,@CurDate)=6 THEN 1 ELSE 0 END)WHEN 7 THEN(CASE WHEN DATEPART(WeekDay,@CurDate)=7 THEN 1 ELSE 0 END)WHEN 8 THEN(CASE WHEN DATEPART(WeekDay,@CurDate) IN (1,2,3,4,5,6,7) THEN 1 ELSE 0 END)WHEN 9 THEN(CASE WHEN DATEPART(WeekDay,@CurDate) IN (2,3,4,5,6) THEN 1 ELSE 0 END)WHEN 10 THEN (CASE WHEN DATEPART(WeekDay,@CurDate) IN (1,7) THEN 1 ELSE 0 END)ELSE 0 END))=1 BEGINSET @Matches = @Matches+1SET @TempDate = @CurDateENDIF @Matches=@Counter AND @Counter<32 BEGINSET @Date=@TempDateENDSET @CurDate=DATEADD(DAY,1,@CurDate)ENDRETURN ISNULL(@Date,@TempDate)ENDGO
CREATE FUNCTION dbo.GetMonthDay(@Month DATETIME,@Type INT, @Counter INT) RETURNS DATETIME AS BEGIN
SET @Month = CONVERT(DATE,DATEADD(DAY,1-DATEPART(DAY,@Month),@Month))
DECLARE @CurDate DATETIME=@Month,@Date DATETIME,@Matches INT=0,@TempDate DATETIME
WHILE @CurDate
IF (SELECT (CASE @Type WHEN 1 THEN (CASE WHEN DATEPART(WeekDay,@CurDate)=1 THEN 1 ELSE 0 END)
WHEN 2 THEN(CASE WHEN DATEPART(WeekDay,@CurDate)=2 THEN 1 ELSE 0 END)
WHEN 3 THEN(CASE WHEN DATEPART(WeekDay,@CurDate)=3 THEN 1 ELSE 0 END)
WHEN 4 THEN(CASE WHEN DATEPART(WeekDay,@CurDate)=4 THEN 1 ELSE 0 END)
WHEN 5 THEN(CASE WHEN DATEPART(WeekDay,@CurDate)=5 THEN 1 ELSE 0 END)
WHEN 6 THEN(CASE WHEN DATEPART(WeekDay,@CurDate)=6 THEN 1 ELSE 0 END)
WHEN 7 THEN(CASE WHEN DATEPART(WeekDay,@CurDate)=7 THEN 1 ELSE 0 END)
WHEN 8 THEN(CASE WHEN DATEPART(WeekDay,@CurDate) IN (1,2,3,4,5,6,7) THEN 1 ELSE 0 END)
WHEN 9 THEN(CASE WHEN DATEPART(WeekDay,@CurDate) IN (2,3,4,5,6) THEN 1 ELSE 0 END)
WHEN 10 THEN (CASE WHEN DATEPART(WeekDay,@CurDate) IN (1,7) THEN 1 ELSE 0 END)
ELSE 0 END))=1 BEGIN
SET @Matches = @Matches+1
SET @TempDate = @CurDate
END
IF @Matches=@Counter AND @Counter<32 BEGIN
SET @Date=@TempDate
SET @CurDate=DATEADD(DAY,1,@CurDate)
RETURN ISNULL(@Date,@TempDate)
GO
The values we have are similar to the sysschedules table but I will list them here;
@Type
@Counter
The steps it follows are as follows;
Below is an example of it in use. This will run every Sunday of February.
You can use it in a IF clause to determine whether or not to run certain parts of a stored procedure.
SELECT Utilities.dbo.GetMonthDay('2020-02-11',1,1) --2020-02-02 00:00:00.000SELECT Utilities.dbo.GetMonthDay('2020-02-11',1,2) --2020-02-09 00:00:00.000SELECT Utilities.dbo.GetMonthDay('2020-02-11',1,3) --2020-02-16 00:00:00.000SELECT Utilities.dbo.GetMonthDay('2020-02-11',1,4) --2020-02-23 00:00:00.000SELECT Utilities.dbo.GetMonthDay('2020-02-11',1,32)--2020-02-23 00:00:00.000IF Utilities.dbo.GetMonthDay('2020-02-11',1,32)='2020-02-23' BEGINSELECT 'Run your code'END
SELECT Utilities.dbo.GetMonthDay('2020-02-11',1,1) --2020-02-02 00:00:00.000
SELECT Utilities.dbo.GetMonthDay('2020-02-11',1,2) --2020-02-09 00:00:00.000
SELECT Utilities.dbo.GetMonthDay('2020-02-11',1,3) --2020-02-16 00:00:00.000
SELECT Utilities.dbo.GetMonthDay('2020-02-11',1,4) --2020-02-23 00:00:00.000
SELECT Utilities.dbo.GetMonthDay('2020-02-11',1,32)--2020-02-23 00:00:00.000
IF Utilities.dbo.GetMonthDay('2020-02-11',1,32)='2020-02-23' BEGIN
SELECT 'Run your code'
I hope this can be of use to you, or it gives you an idea for other processes.
If you need to calculate with holidays then you will need a calendar table, and you can see a great example on the link below.