【问题标题】:How to find overlapping periods recursively in SQL Server如何在 SQL Server 中递归查找重叠时段
【发布时间】:2014-04-16 00:24:40
【问题描述】:

我有这个示例数据集

ID  StartDate   EndDate
------------------------------
1   2014-01-05  2014-01-10
2   2014-01-06  2014-01-11
3   2014-01-07  2014-01-12
4   2014-01-08  2014-01-13
5   2014-01-09  2014-01-14
6   2014-01-26  2014-01-31
7   2014-01-27  2014-02-01
8   2014-01-28  2014-02-02
9   2014-01-29  2014-02-03
10  2014-01-30  2014-02-04

我想选择与提供的时间段重叠的任何行,以及与之重叠的任何行,依此类推。

所以如果我想选择任何超过 2014-01-06 到 2014-01-07 的行数

以下是直接重叠(立即重叠)

1   2014-01-05  2014-01-10
2   2014-01-06  2014-01-11

但我还需要与 1 和 2 重叠的行(子重叠)

3   2014-01-07  2014-01-12
4   2014-01-08  2014-01-13
5   2014-01-09  2014-01-14

如果 3 到 5 有重叠,也返回它们。但在这种情况下,没有。

这是我目前的尝试,但它有两个我不知道如何解决的问题。

;WITH cte
AS 
(
    SELECT  t.ID, 
            t.StartTime, 
            t.EndTime
    FROM    
            dbo.Tasks AS t
    UNION ALL
    SELECT  t.ID, 
            t.StartTime, 
            t.EndTime 
    FROM    
            dbo.Tasks AS t INNER JOIN
            cte AS c ON t.StartTime < c.EndTime
                    AND t.EndTime > c.StartTime

)
SELECT * FROM cte AS a WHERE   a.StartTime < @NewEnd
                    AND a.EndTime > @NewStart

当获取子重叠的重叠时段时,立即重叠被重新包含并导致无限递归。其次,

SELECT * FROM cte AS a WHERE   a.StartTime < @NewEnd
                        AND a.EndTime > @NewStart

where 子句将过滤掉任何递归发现的重叠。

【问题讨论】:

    标签: sql sql-server recursion common-table-expression


    【解决方案1】:

    我会首先确定这些岛屿在您的数据集中的位置,然后才确定哪些岛屿与您的查询范围重叠:

    declare @t table (ID int,StartDate date,EndDate date)
    insert into @t(ID,StartDate,EndDate) values
    (1   ,'20140105','20140110'),
    (2   ,'20140106','20140111'),
    (3   ,'20140107','20140112'),
    (4   ,'20140108','20140113'),
    (5   ,'20140109','20140114'),
    (6   ,'20140126','20140131'),
    (7   ,'20140127','20140201'),
    (8   ,'20140128','20140202'),
    (9   ,'20140129','20140203'),
    (10  ,'20140130','20140204')
    
    declare @Start date
    declare @End date
    select @Start='20140106',@End='20140107'
    
    ;With PotIslands as (
        --Find ranges which aren't overlapped at their start
        select StartDate,EndDate from @t t where
            not exists (select * from @t t2 where
                          t2.StartDate < t.StartDate and
                          t2.EndDate >= t.StartDate)
        union all
        --Extend the ranges by any other ranges which overlap on the end
        select pi.StartDate,t.EndDate
        from PotIslands pi
                inner join
            @t t
                on
                    pi.EndDate >= t.StartDate and pi.EndDate < t.EndDate
    ), Islands as (
        select StartDate,MAX(EndDate) as EndDate from PotIslands group by StartDate
    )
    select * from Islands i where @Start <= i.EndDate and @End >= i.StartDate
    

    结果:

    StartDate  EndDate
    ---------- ----------
    2014-01-05 2014-01-14
    

    如果您需要单独的行,现在可以将选定的岛连接回@t 表以进行简单的范围查询。

    这是因为,例如,如果一个岛内的任何行曾经包含在一个范围内,那么一个岛上的所有剩余行也将总是包括在内。所以我们先找到这些岛屿。

    【讨论】:

      【解决方案2】:

      应该这样做,如果需要,您可以将其放入函数中并使用交叉应用来连接到另一个表。我没有对其进行测试,但它应该能以最小的(如果有的话)错误工作。

      declare @rt table
      (
          ID int not null,
          StartTime date not null,
          EndTime date not null
      )
      
      insert into @rt (ID, StartTime, EndTime)
      select t.*
      from Tasks t
      where (@StartTime <= t.StartTime and @EndTime  > t.StartTime)
          or (@StartTime < t.EndTime and @EndTime  >= t.EndTime)
      
      declare @found int = @@rowcount
      
      while @found > 0
      begin
          insert into @rt (ID, StartTime, EndTime)
          select t.*
          from Tasks t
          left join @rt rt
              on (rt.StartTime <= t.StartTime and rt.EndTime  > t.StartTime)
              or (rt.StartTime < t.EndTime and rt.EndTime  >= t.EndTime)
          where t.ID not in (select ID from @rt)
      
          set @found = @@rowcount
      end
      
      select * from @rt
      

      【讨论】:

        猜你喜欢
        • 2013-08-21
        • 2012-03-18
        • 2014-12-19
        • 2012-12-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-10
        相关资源
        最近更新 更多