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

时间:2016-10-05 12:08:50

标签: sql sql-server-2008 gaps-and-islands

我有工人和他们工作的几个月。我想知道哪些工人有休息时间。考虑一下简单的例子:

enter image description here

在图表上看起来像这样: enter image description here

期望的结果是:

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

假设:

  • 每个工人可能有不同的开始日期和不同的结局 日期。
  • 表格仅包含工人所在的日期(月份) 工作

我欢迎查询,但我很感激只是想法如何做到这一点。我的想法如下。

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

2 个答案:

答案 0 :(得分:3)

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

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

注意:这使用SQL Server语法来表示差异;这只是一种方便,大多数其他数据库具有类似的功能。

答案 1 :(得分:1)

我会使用一些窗函数算法。

输入测试数据:

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');

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

从这里我们只需要做一个计数 - 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语法尝试查询...(不确定它是否可以工作,直到我没有sqls)

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(按键顺序按dte分区)将为worker 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

从这里你可以在worker和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