检查日期范围是否连续或包含每个类别的间隙

Check if date ranges are sequential or contain gaps per category

我有工人和他们工作的月份。我想找出哪些工人有休息时间。考虑简单的例子:

在图表上它看起来像这样:

期望的结果是:

+-----------+-----+
| worker    | gap |
+-----------+-----+
| worker001 | 1   |
+-----------+-----+
| worker002 | 1   |
+-----------+-----+
| worker003 | 0   |
+-----------+-----+

假设:

我欢迎询问,但如果能知道如何去做,我将不胜感激。我的思路如下。

检查每个工作人员的最小和最大日期。为每个工人生成这两天之间的月份序列(不知道如何)。将它加入原始 table 并检查是否有空值。如果是这样,我们就有差距。

计算差距的一种非常简单的方法是计算月数并查看最大值和最小值之间的差异:

select worker,
       (datediff(month, min(month), max(month)) + 1) - count(*) as nummissing
from t
group by worker;

注意:这使用 SQL 服务器语法来区分月份;这只是为了方便,大多数其他数据库都具有类似的功能。

我会选择一些 window 函数算法。

输入测试数据:

create table tt (key varchar(10), dte date);

insert into tt values ('w1', '2017-12-01');
insert into tt values ('w1', '2017-11-01');
insert into tt values ('w1', '2015-12-01');
insert into tt values ('w1', '2015-11-01');
insert into tt values ('w1', '2016-01-01');
insert into tt values ('w1', '2016-02-01');
insert into tt values ('w1', '2016-05-01');
insert into tt values ('w1', '2016-06-01');
insert into tt values ('w2', '2016-02-01');
insert into tt values ('w2', '2016-03-01');
insert into tt values ('w2', '2016-05-01');
insert into tt values ('w3', '2016-01-01');
insert into tt values ('w3', '2016-02-01');

然后我们使用window函数在日期之间创建组以检索连续工作时间。

从这里开始,我们只需要计数 - 1,再按表达式进行分组,即可得到间隙数。

with tmp as (
 select key, 
 dte,
 dte - (row_number() over(partition by key order by dte) MONTHS) as rnk_month, 
 row_number() over(partition by key order by dte) as rnk_tot
 from tt)
select key, min(dte), max(dte), rnk_month 
from tmp
group by key, rnk_month
order by key, rnk_month

正在尝试使用 sqlServer 语法进行查询...(在我没有 sql 之前不确定它是否有效)

with tmp as (
 select key, 
 dte,
 dateadd(month, - row_number() over(partition by key order by dte), dte)  as rnk_month, 
 row_number() over(partition by key order by dte) as rnk_tot
 from tt)
select key, min(dte), max(dte), rnk_month 
from tmp
group by key, rnk_month
order by key, rnk_month

再解释一下:

函数:row_number() over(partition by key order by dte) 将为工人 1 输出:

-----------------------------
Worker | Month     | rnk_tot
-----------------------------
w1     |2015-11-01 | 1
w1     |2015-12-01 | 2
w1     |2016-01-01 | 3
w1     |2016-02-01 | 4
w1     |2016-05-01 | 5
w1     |2016-06-01 | 6
w1     |2017-11-01 | 7
w1     |2017-12-01 | 8

现在,如果我们将 rnk_tot 的日期减去 rnk_tot 视为一个月,我们将得到一些新的日期,这些日期将形成一些连续的组:

----------------------------------------
Worker | Month     | rnk_tot | rnk_month
----------------------------------------
w1     |2015-11-01 | 1       |2015-10-01
w1     |2015-12-01 | 2       |2015-10-01
w1     |2016-01-01 | 3       |2015-10-01
w1     |2016-02-01 | 4       |2015-10-01
w1     |2016-05-01 | 5       |2015-12-01
w1     |2016-06-01 | 6       |2015-12-01
w1     |2017-11-01 | 7       |2017-04-01
w1     |2017-12-01 | 8       |2017-04-01

从这里你可以对工作人员和 rnk_month 列进行分组以收集连续工作时间(这就是我发布的查询所做的):

----------------------------------------
Worker | Mth Min   | Mth Max  | rnk_month
----------------------------------------
w1     |2015-11-01 |2016-02-01|2015-10-01
w1     |2016-05-01 |2016-06-01|2015-12-01
w1     |2017-11-01 |2017-12-01|2017-04-01

从这里你可以得到差距的数量。 所以最终的查询可能是:

with tmp as (
 select key, 
 dateadd(month, - row_number() over(partition by key order by dte), dte) as rnk_month
 from tt)
select key, count(distinct rnk_month) - 1 as gaps
from tmp
group by key

这将为我使用的测试用例提供以下输出:

-----------------
Worker | Gaps
-----------------
w1     | 2
w2     | 1
w3     | 0