检查日期是否介于上一行开始日期和结束日期之间
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),而只会查看开始日期。这有几个好处:
- 即使 rn 序列中存在间隙,它也能使查询正常工作
- 它会发现任务之前间隔中的所有重叠(如果有的话),而不仅仅是前一个间隔。
- 它对开始日期进行排序从而简化了重叠测试。这没什么大不了的。计算很便宜,这不会改变执行计划,并且在 运行 时间内没有明显的区别。但是,嘿,更简单的代码就是更简单的代码。
这依赖的一个假设是存在唯一约束,因此任务中不能有相同的开始日期。这似乎是一个明显的约束,因为您不希望重叠。另外,在 (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 的日期。第二个条目不再与第一个条目重叠——第三个条目重叠。如果您的数据无法做到这一点,请将其设置回去。
我有以下示例数据:
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
6 85569 Joe Bloggs Task 2 Ref001 18/07/2014 12/10/2014 2
因为 18/07/2014
的开始日期与 20/07/14
谁能告诉我如何在不使用光标的情况下做到这一点?
您可以使用 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),而只会查看开始日期。这有几个好处:
- 即使 rn 序列中存在间隙,它也能使查询正常工作
- 它会发现任务之前间隔中的所有重叠(如果有的话),而不仅仅是前一个间隔。
- 它对开始日期进行排序从而简化了重叠测试。这没什么大不了的。计算很便宜,这不会改变执行计划,并且在 运行 时间内没有明显的区别。但是,嘿,更简单的代码就是更简单的代码。
这依赖的一个假设是存在唯一约束,因此任务中不能有相同的开始日期。这似乎是一个明显的约束,因为您不希望重叠。另外,在 (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 的日期。第二个条目不再与第一个条目重叠——第三个条目重叠。如果您的数据无法做到这一点,请将其设置回去。