所有工人的重叠日期范围

问题描述

我想弄清楚如何计算所有工人一起工作的日期范围(岛屿)。换句话说,如果其中一名工人不在某个日期,则应从结果中排除该日期。以下示例数据:

insert into WORK_DAYS(WORKER_ID,DATE_FROM,DATE_TO) VALUES(1,'2019-10-01','2020-04-30');
insert into WORK_DAYS(WORKER_ID,'2020-05-01','2020-07-19');
insert into WORK_DAYS(WORKER_ID,'2020-10-01','9999-01-01');
insert into WORK_DAYS(WORKER_ID,DATE_TO) VALUES(2,'2020-07-31');
insert into WORK_DAYS(WORKER_ID,'2020-11-01',DATE_TO) VALUES(3,'2018-03-12','2018-08-20');
insert into WORK_DAYS(WORKER_ID,'2020-04-15');
insert into WORK_DAYS(WORKER_ID,'2020-07-01','9999-01-01');

sample data

我使用 Firebird 数据库,但您可以在任何数据库(如 sql Server)中显示结果(但请不要交叉应用仅在 sql 标准中定义的内容)。这是已经从间隙/岛屿问题中检索到的简化数据。因为我的样本中的 worker_id 是整个团队。

我知道如何找到重叠的日期范围,但我不知道如何找到同时应用于所有工作人员的重叠日期范围。

解决方法

假设工人没有重叠,您可以为此使用计数技巧。计算每个日期工作的工人数量。那么所有工人的日期就是你想要的日期。

您实际上并不需要每次约会。假设 date_to 作为工作日包括,您可以对数据进行逆透视并使用累积总和。

Postgres 中表达的逻辑中的以下内容(您的问题确实指定了任何数据库中的解决方案都是可以接受的,我发现 Postges 最接近于标准 SQL):

with wd as (
      select worker_id,date_from as dte,1 as inc
      from work_days wd
      union all
      select worker_id,date_to + interval '1 day',-1 as inc
      from work_days wd
     ),wd_cnt as (
      select wd.dte,sum(sum(inc)) over (order by dte) as num_on_date,lead(wd.dte) over (order by wd.dte) as next_dte
      from wd
      group by wd.dte
     )
select dte,next_dte - interval '1 day'
from wd_cnt
where num_on_date = (select count(distinct worker_id) from work_days);

这都是标准 SQL,但日期/时间函数因数据库而异。请注意,这会为 date_to 增加 1 天,因此不要使用该值的绝对最大日期。

Here 是一个 dbfiddle。