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

Check if date falls between previous row start date and end 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

我需要检查任何 Start/End 日期在前一个 rn Start/End 日期之间的位置,其中任务类型相同。

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

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

因为 07/01/2015 的开始日期与 11/01/15

的 rn 3 结束日期重叠
6   85569   Joe Bloggs  Task 2  Ref001  18/07/2014  12/10/2014  2

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

的 rn 1 结束日期重叠

谁能告诉我如何在不使用光标的情况下做到这一点?

您可以使用 lag() 在 SQL Server 2012+ 中执行此操作。在 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;

当处理日期并且您想检查重叠间隔时,计算会稍微复杂一些。考虑对于具有开始日期 [S1, S2] 和结束日期 [E1, E2] 的任意两个间隔 [I1, I2],以下显示了它们可以重叠的所有方式。

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

由此我们可以推导出"no overlap"的等式:

E2 <= S1 OR S2 >= E1

既然你对"yes overlap"感兴趣,只需反转:

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. 它对开始日期进行排序从而简化了重叠测试。这没什么大不了的。计算很便宜,这不会改变执行计划,并且在 运行 时间内没有明显的区别。但是,嘿,更简单的代码就是更简单的代码。

这依赖的一个假设是存在唯一约束,因此任务中不能有相同的开始日期。这似乎是一个明显的约束,因为您不希望重叠。另外,在 (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 的日期。第二个条目不再与第一个条目重叠——第三个条目重叠。如果您的数据无法做到这一点,请将其设置回去。