Using a Tally Table:
SQL Fiddle
DECLARE @month INT,
@year INT
SELECT @month = 8, @year = 2015
--SELECT
-- DATEADD(MONTH, @month - 1, DATEADD(YEAR, @year - 1900, 0)) AS start_day,
-- DATEADD(MONTH, @month, DATEADD(YEAR, @year - 1900, 0)) AS end_d
;WITH CteVacation AS(
SELECT
emp_num,
start_date = CONVERT(DATE, start_date, 101),
end_date = CONVERT(DATE, end_date, 101)
FROM vacation
)
,E1(N) AS(
SELECT * FROM(VALUES
(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)
)t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b),
Tally(N) AS(
SELECT TOP(SELECT MAX(DATEDIFF(DAY, start_date, end_date)) FROM vacation)
ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM E4
)
SELECT
v.emp_num,
COUNT(*)
FROM CteVacation v
CROSS JOIN Tally t
WHERE
DATEADD(DAY, t.N - 1, v.start_date) <= v.end_date
AND DATEADD(DAY, t.N - 1, v.start_date) >= DATEADD(MONTH, @month - 1, DATEADD(YEAR, @year - 1900, 0))
AND DATEADD(DAY, t.N - 1, v.start_date) < DATEADD(MONTH, @month, DATEADD(YEAR, @year - 1900, 0))
GROUP BY v.emp_num
First, you want to use the correct data type to ease your calculation. In my solution, I used a CTE to format your data type. Then build a tally table from 1 up to the max duration of the all the vacations. Using that tally table, do a CROSS JOIN
on the vacation
table to generate all vacation dates from its start_date
up to end_date
.
After that, add a WHERE
clause to filter dates that falls on the passed month
-year
parameter.
Here, @month
and @year
is declared as INT. What you want is to get all dates from the first day of the month
-year
up to its last day. The formula for first day of the month
is:
DATEADD(MONTH, @month - 1, DATEADD(YEAR, @year - 1900, 0))
And for the last day of the month
, add one month to the above and just use <
:
DATEADD(MONTH, @month, DATEADD(YEAR, @year - 1900, 0))