检查日期是否在上一行开始日期和结束日期之间

时间:2015-03-03 23:08:15

标签: sql sql-server sql-server-2008 tsql date

我有以下示例数据:

id  Ref     User        Task    Refernce    Start   End         rn
1   12222   Joe Bloggs  Task 1  Ref001  24/02/2014  20/07/2014  1
2   12568   Joe Bloggs  Task 1  Ref001  25/07/2014  12/10/2014  2
3   14757   Joe Bloggs  Task 1  Ref001  29/10/2014  11/01/2015  3
4   12493   Joe Bloggs  Task 1  Ref001  7/01/2015   6/04/2015   4
5   13694   Joe Bloggs  Task 2  Ref001  3/04/2014   20/07/2014  1
6   85569   Joe Bloggs  Task 2  Ref001  18/07/2014  12/10/2014  2
7   54769   Joe Bloggs  Task 2  Ref001  24/11/2014  5/01/2015   3
8   89716   Joe Bloggs  Task 2  Ref001  12/01/2015  6/04/2015   4

我需要检查任何开始/结束日期在任务类型相同的前一个开始/结束日期之间的位置。

在上述数据中,标记为重叠的行将是:

4   12493   Joe Bloggs  Task 1  Ref001  7/01/2015   6/04/2015   4

因为07/01/2015的开始日期与11/01/15的结束日期重叠

6   85569   Joe Bloggs  Task 2  Ref001  18/07/2014  12/10/2014  2

因为18/07/2014的开始日期与20/07/14

的结束日期重叠

有人可以在不使用光标的情况下告知如何实现这一目标吗?

2 个答案:

答案 0 :(得分:5)

您可以使用SQL Server 2012+中的lag()执行此操作。在SQL Server 2008中,我建议使用join

select s.*,
       (case when s.start between s2.start and s2.end then 1 else 0 end) as flg
from sample s left outer join
     sample sprev
     on s.id = sprev.id + 1;

答案 1 :(得分:0)

使用日期并且您想要检查重叠间隔时,计算有点复杂。考虑到任何两个区间[I1,I2]的起始日期[S1,S2]和结束日期[E1,E2],下面显示了它们可以重叠的所有方式。

I1: S1|----------|E1
I2:         S2|---------|E2

I1:    S1|----|E1
I2: S2|---------|E2

I1: S1|----------|E1
I2:     S2|---|E2

I1:        S1|----------|E1
I2: S2|---------|E2

重叠

时,更容易显示
I1: S1|-------|E1
I2:         S2|-------|E2

I1:         S1|-------|E1
I2: S2|-------|E2

由此我们可以导出“无重叠”的等式:

E2 <= S1 OR S2 >= E1

由于您对“是重叠”感兴趣,只需反转:

E2 > S1 and S2 < E1

或者,如果您订购了日期,那么您知道,S2会始终在S1之后,测试可以简化为

E2 > S1

基于此,这是查询:

select  s1.*, s2.Ref, s2.EndDt PrevEndDt
from    source  s1
join    source  s2
    on  s2.Task     = s1.Task
    and s2.StartDt  < s1.StartDt  -- examine only previous dates
    and s2.EndDt    > s1.StartDt; -- test for overlap

这只显示重叠的间隔。

请注意,查询会忽略任务序列值rn(和ID),而只会查看开始日期。这有几个好处:

  1. 即使rn序列中存在空白和
  2. ,它也会使查询起作用
  3. 它会在任务的前一个时间间隔内找到所有重叠(如果有的话),而不仅仅是前一个时间间隔内的重叠。
  4. 它命令开始日期,从而简化了重叠测试。这不是什么大不了的事。计算很便宜,这不会改变执行计划,并且在运行时间上没有明显的差异。但是,嘿,更简单的代码是更简单的代码。
  5. 这依赖的一个假设是存在唯一约束,以便在任务中不能有相同的开始日期。这似乎是一个明显的限制因为你不想要重叠。加上(Task,StartDt)索引是一个明智的步骤。

    SQLFiddle目前似乎已经失败了。但我的测试脚本并不大。

    create table Source(
      ID        int not null,
      Ref       int not null,
      UserName  varchar( 16 ) not null,
      Task      varchar( 16 ) not null,
      Refernce  varchar( 16 ) not null,
      StartDt   date not null,
      EndDt     date not null,
      rn        smallint not null,
      constraint PK_Source primary key( ID ),
      constraint CK_Source_StartEnd check( StartDt < EndDt)
    );
    
    insert into Source
        select  1, 12222, 'Joe Bloggs', 'Task 1', 'Ref001', '2014-02-24', '2014-07-20', 1 union all
        select  2, 12568, 'Joe Bloggs', 'Task 1', 'Ref001', '2014-07-25', '2014-10-12', 2 union all
        select  3, 14757, 'Joe Bloggs', 'Task 1', 'Ref001', '2014-10-29', '2015-01-11', 3 union all
        select  4, 12493, 'Joe Bloggs', 'Task 1', 'Ref001', '2015-01-07', '2015-04-06', 4 union all
    
        select  5, 13694, 'Joe Bloggs', 'Task 2', 'Ref001', '2015-04-03', '2015-07-20', 1 union all
        select  6, 85569, 'Joe Bloggs', 'Task 2', 'Ref001', '2015-09-24', '2015-10-12', 2 union all
        select  7, 54769, 'Joe Bloggs', 'Task 2', 'Ref001', '2015-07-18', '2015-09-20', 3 union all
        select  8, 89716, 'Joe Bloggs', 'Task 2', 'Ref001', '2016-01-12', '2016-04-06', 4;
    
    create unique index UQ_Source_TaskStart on Source( Task, StartDt );
    

    注意我稍微改变了任务2的日期。第二个条目不再与第一个条目重叠 - 第三个条目。如果您的数据不可能,请将其重新设置。