Having a calendar table with a list of all possible dates is convenient, but in this case we can do without it.
I will summarize your question a bit. Instead of looking only at the current quarter, let you have two parameters that determine the range of dates of interest to you:
DECLARE @ParamStartDate date; DECLARE @ParamEndDate date;
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'
The two periods A and B overlap when (StartA <= EndB)
and (EndA >= StartB)
.
Then we need to calculate how many days are at the intersection of two periods.
The intersection period cannot be greater than the specified date range (from @ParamStartDate
to @ParamEndDate
).
The intersection period cannot be longer than the duration of the disease ( FromDate
to UntilDate
).
So, the start of the intersection is the last of FromDate
and @ParamStartDate
, i.e. MAX(FromDate, @ParamStartDate)
The end of the intersection is the earliest of UntilDate
and @ParamEndDate
, i.e. MIN(UntilDate, @ParamEndDate)
Finally, the duration of the crossing in days
DATEDIFF(day, MAX(FromDate, @ParamStartDate), MIN(UntilDate, @ParamEndDate))
But, only if it is positive. If it is negative, it means that the period of the illness ended before the quarter began (or the illness began after the end of the quarter).
There are no built-in MIN, MAX functions that take two parameters as needed, so I use CROSS APPLY
to calculate them. In addition, I calculate the number of days in a given quarter, just for completeness. The final request 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'
I add 1 to DATEDIFF
to get the duration of one day if the start and end dates of the period are the same.