Having a Calendar table with the list of all possible dates is handy, but in this case we can do without it.
I'll generalize your question a bit. Instead of looking just at the current quarter let's have two parameters that define the range of dates that you are interested in:
DECLARE @ParamStartDate date;
DECLARE @ParamEndDate date;
At first we need to get all rows from Absence
that have a range from FromDate
to UntilDate
that intersects with the given period.
SELECT
...
FROM
Absence
WHERE
ABS_REASON='SICK'
-- all absence periods, which overlap with the given period
AND FromDate <= @ParamEndDate
AND UntilDate >= @ParamStartDate
Two periods A and B overlap when (StartA <= EndB)
and (EndA >= StartB)
.
Then we need to calculate how many days are in the intersection of the two periods.
The intersection period can't be larger than the given range of dates (@ParamStartDate
to @ParamEndDate
).
The intersection period can't be larger than the duration of the sickness (FromDate
to UntilDate
).
So, the beginning of the intersection is the latest of FromDate
and @ParamStartDate
, i.e. MAX(FromDate, @ParamStartDate)
The ending of the intersection is the earliest of UntilDate
and @ParamEndDate
, i.e. MIN(UntilDate, @ParamEndDate)
Finally, the duration of the intersection in days is
DATEDIFF(day, MAX(FromDate, @ParamStartDate), MIN(UntilDate, @ParamEndDate))
But, only if it is positive. If it is negative, it means that sickness period ended before the given quarter started (or sickness started after the given quarter ended).
There is no built-in MIN, MAX functions that take two parameters as I need, so I use CROSS APPLY
to calculate them. Also, I calculate the number of days in the given quarter, just for completeness. The final query looks like this:
SELECT
1+DATEDIFF(day, @ParamStartDate, @ParamEndDate) AS QuarterDays
,CASE WHEN 1+DATEDIFF(day, CTE_MaxStartDate.AbsenceStartDate, CTE_MinEndDate.AbsenceEndDate) > 0
THEN 1+DATEDIFF(day, CTE_MaxStartDate.AbsenceStartDate, CTE_MinEndDate.AbsenceEndDate)
ELSE 0 END AS AbsenceDays
FROM
Absence
CROSS APPLY
(
SELECT CASE WHEN UntilDate < @ParamEndDate THEN UntilDate ELSE @ParamEndDate END AS AbsenceEndDate
) AS CTE_MinEndDate
CROSS APPLY
(
SELECT CASE WHEN FromDate > @ParamStartDate THEN FromDate ELSE @ParamStartDate END AS AbsenceStartDate
) AS CTE_MaxStartDate
WHERE
ABS_REASON='SICK'
-- all absence periods, which overlap with the given period
AND FromDate <= @ParamEndDate
AND UntilDate >= @ParamStartDate
I add 1 to DATEDIFF
to get a duration of one day if start and end dates of the period are the same.