拥有一个包含所有可能日期列表的日历表很方便,但在这种情况下,我们可以不用它。
我会概括一下您的问题。让我们有两个参数来定义您感兴趣的日期范围,而不是只查看当前季度:
DECLARE @ParamStartDate date;
DECLARE @ParamEndDate date;
首先,我们需要从Absence 获取范围从FromDate 到UntilDate 且与给定时间段相交的所有行。
SELECT
...
FROM
Absence
WHERE
ABS_REASON='SICK'
-- all absence periods, which overlap with the given period
AND FromDate <= @ParamEndDate
AND UntilDate >= @ParamStartDate
(StartA <= EndB) 和 (EndA >= StartB) 时,两个句点 A 和 B 重叠。
然后我们需要计算两个时期的交集有多少天。
相交时间段不能大于给定的日期范围(@ParamStartDate 到 @ParamEndDate)。
交叉期不能大于疾病的持续时间(FromDate 到 UntilDate)。
所以,相交的开始是FromDate和@ParamStartDate中最晚的一个,即MAX(FromDate, @ParamStartDate)
路口的终点是UntilDate和@ParamEndDate中最早的,即MIN(UntilDate, @ParamEndDate)
最后,以天为单位的路口持续时间为
DATEDIFF(day, MAX(FromDate, @ParamStartDate), MIN(UntilDate, @ParamEndDate))
但是,只有当它是积极的。如果为负数,则表示病假期在给定季度开始之前结束(或病假在给定季度结束后开始)。
没有内置的 MIN、MAX 函数可以根据需要使用两个参数,所以我使用CROSS APPLY 来计算它们。另外,为了完整起见,我计算了给定季度的天数。最终查询如下所示:
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
如果期间的开始日期和结束日期相同,我将 1 添加到 DATEDIFF 以获得一天的持续时间。