Logically, this should work, but it probably isn't the most elegant solution.
After creating a bit of sample data, I gather all of the 'suspect' days including known sick days, known holidays and weekends that are adjacent to either a sick day or a holiday. Then I identify the start and end of each group of consecutive days and for each employee count the start dates of ranges that contain a sick day.
/***** SAMPLE DATA *****/
declare @sick table (
empid int,
sick datetime
)
declare @holiday table (
holiday datetime
)
/* Example 1 */
insert into @sick values (1,'2010/01/04'); /* Mon */
insert into @sick values (1,'2010/01/05'); /* Tue */
/* Example 2 */
insert into @sick values (1,'2010/01/15'); /* Fri */
insert into @sick values (1,'2010/01/18'); /* Mon */
/* Example 3 */
insert into @sick values (1,'2010/01/21'); /* Thu */
insert into @holiday values('2010/01/22'); /* Fri */
insert into @sick values (1,'2010/01/25'); /* Mon */
/* Extra Examples */
insert into @sick values (3,'2010/01/08');
insert into @sick values (2,'2010/01/08');
insert into @holiday values ('2010/01/11');
insert into @sick values (3,'2010/01/20');
insert into @sick values (3,'2010/01/21');
/* Extra Holiday */
insert into @holiday values ('2010/02/05');
/***** SAMPLE DATA *****/
/* First a CTE to gather all of the 'suspect' days together
including known sick days, known holidays and weekends
that are adjacent to either a sick day or a holiday */
with suspectdays as (
/* Start with all Sick days */
select
empid,
sick dt,
'sick' [type]
from
@sick
/* Add all Saturdays following a sick Friday */
union
select
empid,
DATEADD(day,1,sick) dt,
'weekend' [type]
from
@sick
where
(DATEPART(WEEKDAY,sick) + @@DATEFIRST) % 7 = 6
/* Add all Sundays following a sick Friday */
union
select
empid,
DATEADD(day,2,sick) dt,
'weekend' [type]
from
@sick
where
(DATEPART(WEEKDAY,sick) + @@DATEFIRST) % 7 = 6
/* Add all Sundays preceding a sick Monday */
union
select
empid,
DATEADD(day,-1,sick) dt,
'weekend' [type]
from
@sick
where
(DATEPART(WEEKDAY,sick) + @@DATEFIRST) % 7 = 2
/* Add all Saturdays preceding a sick Monday */
union
select
empid,
DATEADD(day,-2,sick) dt,
'weekend' [type]
from
@sick
where
(DATEPART(WEEKDAY,sick) + @@DATEFIRST) % 7 = 2
/* Add all Holidays */
union
select
empid,
holiday dt,
'holiday' [type]
from
@holiday,
(select distinct empid from @sick) as a
/* Add all Saturdays following a holiday Friday */
union
select
empid,
DATEADD(day,1,holiday) dt,
'weekend' [type]
from
@holiday,
(select distinct empid from @sick) as a
where
(DATEPART(WEEKDAY,holiday) + @@DATEFIRST) % 7 = 6
/* Add all Sundays following a holiday Friday */
union
select
empid,
DATEADD(day,2,holiday) dt,
'weekend' [type]
from
@holiday,
(select distinct empid from @sick) as a
where
(DATEPART(WEEKDAY,holiday) + @@DATEFIRST) % 7 = 6
/* Add all Sundays preceding a holiday Monday */
union
select
empid,
DATEADD(day,-1,holiday) dt,
'weekend' [type]
from
@holiday,
(select distinct empid from @sick) as a
where
(DATEPART(WEEKDAY,holiday) + @@DATEFIRST) % 7 = 2
/* Add all Saturdays preceding a holiday Monday */
union
select
empid,
DATEADD(day,-2,holiday) dt,
'weekend' [type]
from
@holiday,
(select distinct empid from @sick) as a
where
(DATEPART(WEEKDAY,holiday) + @@DATEFIRST) % 7 = 2
),
/* Now a CTE to identify the start and end of each
group of consecutive days for each employee */
suspectranges as (
select distinct
sd.empid,
( select
max(dt)
from
suspectdays
where
empid = sd.empid and
DATEADD(day,-1,dt) not in (select dt from suspectdays where empid = sd.empid) and
dt <= sd.dt
) rangeStart,
( select
min(dt)
from
suspectdays
where
empid = sd.empid and
DATEADD(day,1,dt) not in (select dt from suspectdays where empid = sd.empid) and
dt >= sd.dt
) rangeEnd
from
suspectdays sd
)
/* For each employee count the start dates of ranges that contain a sick day */
select
empid,
COUNT(rangeStart) SickIncidents
from
suspectranges sr
where
exists (select * from suspectdays where dt between sr.rangeStart and sr.rangeEnd and empid=sr.empid and type='sick')
group by
empid
For the sample data I created, here's the result.
empid SickIncidents
----------- -------------
1 3
2 1
3 2