I just wrote a T-SQL query based on CTE that generates support information to calculate working days in a period. Simply look at the WorkingDaySequential measure, which difference between two dates is the number of elapsed working days.
In the real world:
- The Holidays CTE could be replaced by a real table with user-inserted data.
- The temporary [#Calendar] could be a real table, providing necessary data access from DM
and probably much else.
The following code is provided “as is”, without any warranty about its behavior.
UPDATE 2007-11-28 – fixed two missing conditions in the query
SET STATISTICS IO ON
GO
SET DATEFIRST 7 -- Default is 7 (US / Sunday)
GO
DECLARE @StartYear AS INT DECLARE @EndYear AS INT
SET @StartYear = 2006;
SET @EndYear = 2015;
WITH Holidays AS (
-- Italian Holidays (use year 1900 for recurrencies dates)
SELECT Date = CAST('19000101' AS DATETIME)
UNION ALL SELECT Date = CAST('19000106' AS DATETIME)
UNION ALL SELECT Date = CAST('19000425' AS DATETIME)
UNION ALL SELECT Date = CAST('19000501' AS DATETIME)
UNION ALL SELECT Date = CAST('19000602' AS DATETIME)
UNION ALL SELECT Date = CAST('19000815' AS DATETIME)
UNION ALL SELECT Date = CAST('19001101' AS DATETIME)
UNION ALL SELECT Date = CAST('19001208' AS DATETIME)
UNION ALL SELECT Date = CAST('19001225' AS DATETIME)
UNION ALL SELECT Date = CAST('19001226' AS DATETIME)
UNION ALL -- Holidays changing date each year
SELECT Date = CAST('20060417' AS DATETIME)
UNION ALL SELECT Date = CAST('20070409' AS DATETIME)
),
Years AS (
SELECT YYYY = @StartYear
UNION ALL
SELECT YYYY + 1 FROM Years WHERE YYYY < @EndYear
),
Months AS (
SELECT MM = 1
UNION ALL
SELECT MM + 1 FROM Months WHERE MM < 12
),
Days AS (
SELECT DD = 1
UNION ALL
SELECT DD + 1 FROM Days WHERE DD < 31
),
DatesRaw AS (
SELECT
YYYY = YYYY,
MM = MM,
DD = DD,
ID_Date = YYYY * 10000 + MM * 100 + DD,
DateString = CAST(YYYY * 10000 + MM * 100 + DD AS VARCHAR),
Date = CASE WHEN ISDATE(YYYY * 10000 + MM * 100 + DD) = 1 THEN CAST(
CAST(YYYY * 10000 + MM * 100 + DD AS VARCHAR) AS DATETIME
) ELSE NULL END
FROM
Years
CROSS JOIN Months
CROSS JOIN Days
WHERE
ISDATE(YYYY * 10000 + MM * 100 + DD) = 1
)
SELECT
DatesRaw.*,
DayOfWeek = DATEPART(dw, DatesRaw.Date),
CalendarDaySequential = CAST(DatesRaw.Date AS INT),
WorkingDay = CAST(
CASE DATEPART(dw, DatesRaw.Date)
WHEN 1 THEN 0 -- Sunday
WHEN 7 THEN 0 -- Saturday
ELSE CASE
WHEN recurring.Date IS NULL AND fixed.Date IS NULL THEN 1
ELSE 0 END
END AS BIT
) INTO #Calendar
FROM
DatesRaw
LEFT JOIN Holidays recurring ON recurring.Date = DATEADD(
Year,
1900 - YEAR(DatesRaw.Date),
DatesRaw.Date
)
LEFT JOIN Holidays fixed ON fixed.Date = DatesRaw.Date
ORDER BY ID_Date
GO
SELECT *, WorkingDaySequential = ( SELECT COUNT(WorkingDay) FROM #Calendar wd3 WHERE wd3.CalendarDaySequential <= wd1.CalendarDaySequential AND wd3.WorkingDay = 1 ) FROM #Calendar wd1 ORDER BY ID_Date
GO