add batch remove batch split batch comment selection show hidden batches hide batch highlight batch
db<>fiddle
donate feedback about
By using db<>fiddle, you agree to license everything you submit by Creative Commons CC0.
Help with an interesting Postgres question: Why isn't an Index Only Scan used on a partition accessed via the parent table?.
create table calendar (
cal_date date not null primary key
);

create table holiday (
worker_id int not null,
date_of_holiday date not null,
primary key (worker_id, date_of_holiday)
);

insert into holiday
(worker_id, date_of_holiday) values
(1, '2021-10-15');

create table worker (
id int not null,
workingday date not null,
primary key (id, workingday)
);
1 rows affected
--
-- filling the calendar with the current and next year
--
with recursive rcte_calendar as
(
select date_trunc('year', cast(current_date as date)) as day
, date_trunc('year', cast(current_date as date))
+ interval '2 year'
- interval '1 day' as maxday
union all
select day + interval '1 day', maxday
from rcte_calendar
where day < maxday
)
insert into calendar (cal_date)
select day
from rcte_calendar cte
where not exists (
select 1
from calendar cal
where cal.cal_date = cte.day
);

-- what range does the calendar have
select
date_part('year', cal_date) as year
, min(cal_date)
, max(cal_date)
from calendar
group by date_part('year', cal_date)
order by year;
730 rows affected
year min max
2021 2021-01-01 2021-12-31
2022 2022-01-01 2022-12-31
--
-- putting test data in worker
--
insert into worker (id, workingday)
select wrkr.worker_id, cal.cal_date
from calendar as cal
cross join (values (1),(2),(3)) wrkr(worker_id)
where cal.cal_date between cast('2021-10-10' as date)
and cast('2021-10-22' as date)
and extract(dow from cal.cal_date) not in (0, 6) -- no weekends
and not exists (
select 1
from holiday as hol
where hol.worker_id = wrkr.worker_id
and hol.date_of_holiday = cal.cal_date
)
and not exists (
select 1
from worker as wrkr2
where wrkr2.id = wrkr.worker_id
and wrkr2.workingday = cal.cal_date
);

--
-- worker 2 wasn't working on some days, so remove those
--
delete from worker
where id = 2
and workingday in ('2021-10-15','2021-10-18','2021-10-19');
29 rows affected
3 rows affected
--
-- how does it look so far
--
select *
, extract(dow from workingday) as dayofweek
from worker
id workingday dayofweek
1 2021-10-11 1
2 2021-10-11 1
3 2021-10-11 1
1 2021-10-12 2
2 2021-10-12 2
3 2021-10-12 2
1 2021-10-13 3
2 2021-10-13 3
3 2021-10-13 3
1 2021-10-14 4
2 2021-10-14 4
3 2021-10-14 4
3 2021-10-15 5
1 2021-10-18 1
3 2021-10-18 1
1 2021-10-19 2
3 2021-10-19 2
1 2021-10-20 3
2 2021-10-20 3
3 2021-10-20 3
1 2021-10-21 4
2 2021-10-21 4
3 2021-10-21 4
1 2021-10-22 5
2 2021-10-22 5
3 2021-10-22 5
--
-- The missing days, based on days other workers filled in
-- But without considering the holidays
--
select d.days, wrkr.worker_id
from (select distinct workingday as days from worker) d
cross join (select distinct id as worker_id from worker) wrkr
left join worker as ent
on ent.workingday = d.days
and ent.id = wrkr.worker_id
where ent.id is null
group by d.days, wrkr.worker_id
days worker_id
2021-10-15 1
2021-10-15 2
2021-10-18 2
2021-10-19 2
--
-- Missing days, based on period and holidays
--
select cal.cal_date, wrkr.id as worker_id
, count(case when ent.id is null then 1 end)
over (partition by wrkr.id order by cal.cal_date) as rolling_count
from calendar as cal
cross join (select distinct id from worker) wrkr
left join worker as ent
on ent.workingday = cal.cal_date
and ent.id = wrkr.id
where cal.cal_date between cast('2021-10-10' as date)
and cast('2021-10-22' as date)
and extract(dow from cal.cal_date) not in (0, 6) -- no weekends
and not exists ( -- workers may be on holiday
select 1
from holiday as hol
where hol.worker_id = wrkr.id
and hol.date_of_holiday = cal.cal_date
)
and ent.id is null -- the unmatched
group by cal.cal_date, wrkr.id, ent.id
order by cal.cal_date, wrkr.id
cal_date worker_id rolling_count
2021-10-15 2 1
2021-10-18 2 2
2021-10-19 2 3
--
-- Missing day ranges, based on period and holidays
--
with cte_missed_work as
(
select
cal.cal_date
, wrkr.id as worker_id
, lag(cal_date) over (partition by wrkr.id
order by cal.cal_date) as prev_date
from calendar as cal
cross join (select distinct id from worker) wrkr
left join worker as ent
on ent.workingday = cal.cal_date
and ent.id = wrkr.id
where cal.cal_date between cast('2021-10-10' as date)
and cast('2021-10-22' as date)
and extract(dow from cal.cal_date) not in (0, 6) -- no weekends
and not exists ( -- workers may be on holiday
select 1
from holiday as hol
where hol.worker_id = wrkr.id
and hol.date_of_holiday = cal.cal_date
)
and ent.id is null -- the unmatched
group by cal.cal_date, wrkr.id
),
cte_missed_work2 as (
select *
, sum(case when date_part('day', cal_date::timestamp - prev_date::timestamp) = 1 then 0 else 1 end)
over (partition by worker_id order by cal_date) as Rnk
from cte_missed_work
)
select worker_id
, min(cal_date) as firstDate
, max(cal_date) as lastDate
worker_id firstdate lastdate missing_days
2 2021-10-15 2021-10-15 1
2 2021-10-18 2021-10-19 2