在sql server中需要检查日期是否重叠

时间:2017-06-03 11:52:28

标签: sql sql-server

以下是我的输入代码

ID  EMP_ID       PROJECT_NAME            START_DATE          END_DATE
1   10016351    ABC                       22-12-2016        15-05-2017
2   10016351    ABC                       01-09-2016        22-11-2016
1   10081503    RTBS AMaaS                21-11-2016        15-02-2017
2   10081503    RTBS AMaaS                18-07-2016        25-11-2016
3   10081503    RTBS AMaaS                21-08-2016        13-10-2016
4   10081503    RTBS AMaaS                03-02-2015        22-05-2015
1   10089293    RTBS PDaaS                17-02-2017        31-12-2017
2   10089293    RTBS PDaaS                13-06-2016        14-02-2017

我需要检查员工,日期是否重叠。如果是,那么它将返回最小开始日期和最大结束日期,并标记为' O'。

例如,对于emp_id = 10081503,日期重叠 2016年11月21日至2016年7月18日至2016年11月25日也是如此 2016年8月21日至2016年6月18日至2016年11月25日之间 所以查询应该将开始日期返回为18-07-2016(分钟)和结束日期为  15-02-2017(最大)日期重叠。 对于休息条目,它应返回带有标记' N'的相同条目。

EMP_ID | PROJECT_NAME | START_DATE | END_DATE |旗 10081503 | RTBS AMaaS | 21-11-2016 | 15-02-2017 | Ø 10081503 | RTBS AMaaS | 03-02-2015 | 22-05-2015 | Ñ

同样明智的我的最终输出应该是,

ID  EMP_ID       PROJECT_NAME            START_DATE          END_DATE   FLAG
1   10016351    ABC                       22-12-2016        15-05-2017   N
2   10016351    ABC                       01-09-2016        22-11-2016   N
1    10081503   RTBS AMaaS                21-11-2016        15-02-2017   O
2    10081503   RTBS AMaaS                03-02-2015        22-05-2015   N
1   10089293    RTBS PDaaS                17-02-2017        31-12-2017   N
2   10089293    RTBS PDaaS                13-06-2016        14-02-2017   N

3 个答案:

答案 0 :(得分:1)

使用outer apply()返回重叠的第一个id并按该值进行分组。使用row_number()重新编号id

select
    id = row_number() over (
      partition by t.emp_id 
      order by min(start_date) desc
      )
  , t.emp_id
  , t.project_name
  , start_date = min(start_date)
  , end_date   = max(end_date)
  , flag = max(case when x.id <> t.id then 'O' else 'N' end)
from t
  outer apply (
    select top 1 i.id
    from t i
    where i.Emp_Id = t.Emp_id
      and i.End_Date > t.Start_Date
      and t.End_Date > i.Start_Date
    order by i.Start_Date
      ) x
group by t.emp_id, t.project_name, x.id

rextester演示:http://rextester.com/VHMZ91714

返回:

+----+----------+--------------+------------+------------+------+
| id |  emp_id  | project_name | start_date |  end_date  | flag |
+----+----------+--------------+------------+------------+------+
|  1 | 10016351 | ABC          | 2016-12-22 | 2017-05-15 | N    |
|  2 | 10016351 | ABC          | 2016-09-01 | 2016-11-22 | N    |
|  1 | 10081503 | RTBS AMaaS   | 2016-07-18 | 2017-02-15 | O    |
|  2 | 10081503 | RTBS AMaaS   | 2015-02-03 | 2015-05-22 | N    |
|  1 | 10089293 | RTBS PDaaS   | 2017-02-17 | 2017-12-31 | N    |
|  2 | 10089293 | RTBS PDaaS   | 2016-06-13 | 2017-02-14 | N    |
+----+----------+--------------+------------+------------+------+

以上将折叠相互重叠的范围,但如果您需要将仅在边缘重叠的多个范围折叠到一个范围内,我们可以使用日历或日期表,如下所示:

对于内存中只有152kb,您可以在表格中拥有30年的日期:

/* dates table */
declare @fromdate date = '20000101';
declare @years    int  = 30;
/* 30 years, 19 used data pages ~152kb in memory, ~264kb on disk */
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
select top (datediff(day, @fromdate,dateadd(year,@years,@fromdate)))
    [Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,@fromdate))
into dbo.Dates
from n as deka cross join n as hecto cross join n as kilo
               cross join n as tenK  cross join n as hundredK
order by [Date];
create unique clustered index ix_dbo_Dates_date on dbo.Dates([Date]);

如果您想要创建日期表,您可以使用此查询生成如下日期:

declare @fromdate date = '20100101';
declare @thrudate date = getdate();
;with n as (select n from (values(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) t(n))
, dates as (
  select top (datediff(day, @fromdate, @thrudate)+1) 
      [Date]=convert(date,dateadd(day,row_number() over(order by (select 1))-1,@fromdate))
  from n as deka cross join n as hecto cross join n as kilo
                cross join n as tenK cross join n as hundredK
   order by [Date]
)
, cte as (
  select
      t.emp_id
    , t.project_name
    , d.date
    , rn = row_number() over (partition by t.emp_id, t.project_name order by d.date)-1
    , flag = case when count(*) > 1 then 'O' else 'N' end  
  from t
    inner join dates d
      on d.date >= t.start_date
     and d.date <= t.end_date
  group by t.emp_id, t.project_name, d.date
)
select 
    id = row_number() over (partition by emp_id order by min(date) desc)
  , emp_id
  , project_name
  , start_date = min(date)
  , end_date = max(date)
  , flag = max(flag)
from cte
group by emp_id, project_name, dateadd(day,-rn,date)

rextester演示:http://rextester.com/QKEMH32326

返回:

+----+----------+--------------+------------+------------+------+
| id |  emp_id  | project_name | start_date |  end_date  | flag |
+----+----------+--------------+------------+------------+------+
|  1 | 10016351 | ABC          | 2016-12-22 | 2017-05-15 | N    |
|  2 | 10016351 | ABC          | 2016-09-01 | 2016-11-22 | N    |
|  1 | 10081503 | RTBS AMaaS   | 2016-07-18 | 2017-02-15 | O    |
|  2 | 10081503 | RTBS AMaaS   | 2015-02-03 | 2015-05-22 | N    |
|  1 | 10089293 | RTBS PDaaS   | 2017-02-17 | 2017-12-31 | N    |
|  2 | 10089293 | RTBS PDaaS   | 2016-06-13 | 2017-02-14 | N    |
+----+----------+--------------+------------+------------+------+

日历和数字表参考:

答案 1 :(得分:0)

谢谢大家的帮助。

在相同的背景下需要一些帮助,

遇到下面的情景, enter image description here

在这种情况下,我们将在ALLOCATION DETAILS中获得两个条目。这是因为资源被分配给两个不同的项目,但第一个分配项目的分配结束日期(在上面的例子中)与第二个分配项目的分配开始日期重叠。 因此,对于分配1的任期计算: 任期(Alloc 1)= Alloc开始日期(Alloc 2) - Alloc开始日期(Alloc 1)     即,(17-11-2016) - (01-09-2016)= 77 分配2的任期计算没有变化。 在这里,最终条目如下所示:

[1]: https://i.stack.imgur.com/DSkjd.jpg

答案 2 :(得分:0)

不幸的是,第一个解决方案无法按所述方式运行,运行代码将仅导致三行(代码组甚至不重叠的日期):

-------------------------------------------------------------------
idItem      qty    shipToAddLine    shipToCityStZip idItem      qty
-------------------------------------------------------------------
DUR 82674   1                       ZipCode         DUR 82674   1
DUR 82674   1                       ZipCode         DUR 82674   1
DUR 82674   1                       ZipCode         DUR 82674   1

谁能提供可行的解决方案? 以下是一个示例代码,该代码无需创建任何表即可运行,仅用于测试:

id  emp_id  start_date  end_date    flag
1   10016351    2016-09-01  2017-05-15  O
1   10081503    2015-02-03  2017-02-15  O
1   10089293    2016-06-13  2017-12-31  O