仅选择不在另一条记录的时间范围内启动的记录

时间:2014-03-17 22:27:18

标签: sql date sql-server-2005

我尝试使用MS SQL Server 2005实现以下目标,但不知道该怎么做。

目标是仅选择与锚记录不在同一时间段内开始的记录。

具有相同ID的行是一个组,并作为该组的一部分进行评估。

从基于StartDate的最早日期(A)开始,与具有相同ID的下一行(B)进行比较。

如果B在A中开始,则将B标记为无效。继续将A与具有相同ID的所有剩余记录进行比较。将A中的任何一个开头标记为无效。

将与A不重叠的下一条记录标记为有效。现在重复上述相同的过程(即检查是否有任何后续记录在新的有效记录的时间范围内开始)。

重复此过程,直到分析完所有记录。

示例:创建下表。

if object_id ('tempdb..#Dates') is not null drop table #Dates

create table #Dates (ID int, StartDate datetime, EndDate datetime)
Insert into #Dates
Select 1, '7/23/2003'   ,   '8/22/2003'  union all
select 1, '8/21/2003'   ,   '11/19/2003' union all
select 1, '11/18/2003'  ,   '12/18/2003' union all
select 1, '12/17/2003'  ,   '1/16/2004'  union all
select 1, '1/15/2004'   ,   '2/14/2004'  union all
select 1, '2/11/2004'   ,   '2/26/2004'  union all
select 1, '9/14/2004'   ,   '10/14/2004' union all
select 1, '10/5/2004'   ,   '10/20/2004' union all
select 1, '11/20/2004'  ,   '12/20/2004' union all
select 1, '12/19/2004'  ,   '1/18/2005'  union all
select 1, '1/12/2005'   ,   '1/27/2005'  union all
select 1, '2/27/2005'   ,   '3/11/2005'  

应用重叠逻辑规则后的预期输出:

ID  StartDate   EndDate     Valid
--  ---------   ---------   -----
1   7/23/2003   8/22/2003   1
1   8/21/2003   11/19/2003  0
1   11/18/2003  12/18/2003  1
1   12/17/2003  1/16/2004   0
1   1/15/2004   2/14/2004   1
1   2/11/2004   2/26/2004   0
1   9/14/2004   10/14/2004  1
1   10/5/2004   10/20/2004  0
1   11/20/2004  12/20/2004  1
1   12/19/2004  1/18/2005   0
1   1/12/2005   1/27/2005   1
1   2/27/2005   3/11/2005   1

1 个答案:

答案 0 :(得分:0)

我想出了如何回答我自己的问题。使用row_number对记录进行排序后使用递归SQL。

if object_id ('tempdb..#Dates') is not null drop table #Dates

create table #Dates (ID int, StartDate datetime, EndDate datetime)
Insert into #Dates
Select 1, '7/23/2003'   ,   '8/22/2003'  union all
select 1, '8/21/2003'   ,   '11/19/2003' union all
select 1, '11/18/2003'  ,   '12/18/2003' union all
select 1, '12/19/2004'  ,   '1/18/2005'  union all
select 1, '1/12/2005'   ,   '1/27/2005'  union all
select 1, '2/27/2005'   ,   '3/11/2005'  union all
select 1, '12/17/2003'  ,   '1/16/2004'  union all
select 1, '1/15/2004'   ,   '2/14/2004'  union all
select 1, '2/11/2004'   ,   '2/26/2004'  union all
select 1, '9/14/2004'   ,   '10/14/2004' union all
select 1, '10/5/2004'   ,   '10/20/2004' union all
select 1, '11/20/2004'  ,   '12/20/2004'


--Phase 1:  Apply ordering to dates
if object_id ('tempdb..#OrderedRecords') is not null drop table #OrderedRecords 

select *, N = row_number () over (partition by ID order by StartDate asc, EndDate desc)
into #OrderedRecords
from #Dates


--Phase 2:  Apply Overlap Rules (Subsume records that overlap)    
;with Subsume (ID, N, StartDate, EndDate, IntermediateStartDate, IntermediateEndDate, Valid) as
(
    select  ID, N, StartDate, EndDate, IntermediateStartDate = StartDate, IntermediateEndDate = EndDate,         
            Valid = 1
    from    #OrderedRecords 
    where   N = 1
    UNION ALL   
    select  c.ID, c.N, y.StartDate, y.EndDate,
            IntermediateStartDate = case when c.StartDate between y.IntermediateStartDate and y.IntermediateEndDate then y.IntermediateStartDate else c.StartDate end, 
            IntermediateEndDate  = case when c.StartDate between y.IntermediateStartDate and y.IntermediateEndDate then y.IntermediateEndDate else c.EndDate end,               
            Valid = case when (c.StartDate between y.IntermediateStartDate and y.IntermediateEndDate) then 0 else 1 end
    from    #OrderedRecords c
    join    Subsume y
            on  y.ID = c.ID
            and y.N = c.n - 1
            and y.IntermediateStartDate >= c.EndDate
    UNION ALL   
    select  c.ID, c.N, c.StartDate, c.EndDate,
            IntermediateStartDate = case when c.StartDate between y.IntermediateStartDate and y.IntermediateEndDate then y.IntermediateStartDate else c.StartDate end, 
            IntermediateEndDate  = case when c.StartDate between y.IntermediateStartDate and y.IntermediateEndDate then y.IntermediateEndDate else c.EndDate end,
            Valid = case when (c.StartDate between y.IntermediateStartDate and y.IntermediateEndDate) then 0 else 1 end
    from    #OrderedRecords c
    join    Subsume y
            on  y.ID = c.ID
            and y.N = c.n - 1
            and y.IntermediateStartDate < c.EndDate
)

Select ID, StartDate, EndDate, Valid
from Subsume
OPTION (MAXRECURSION 0)