【问题标题】:What is a good way to find gaps in a set of datespans?在一组日期跨度中找到间隙的好方法是什么?
【发布时间】:2011-01-22 01:13:11
【问题描述】:

有什么方法可以在一组日期跨度中找到间隙?

例如,我有这些日期跨度:

1/ 1/11 - 1/10/11  
1/13/11 - 1/15/11  
1/20/11 - 1/30/11

那么我的开始和结束日期分别是 2011 年 1 月 7 日和 2011 年 1 月 14 日。

我想知道在 2011 年 1 月 10 日和 11 年 1 月 13 日之间存在间隔,因此无法确定开始日期和结束日期。或者我只想返回遇到的第一个间隙的日期跨度。

如果这可以在 SQL Server 中完成就好了。

我正在考虑查看每个日期,以了解它是否落在日期跨度内......如果没有,那么那天会有一个间隙。

【问题讨论】:

    标签: sql sql-server gaps-and-islands


    【解决方案1】:
    • 跳转到倒数第二个代码块*I want to be able to tell that between 1/10/11 and 1/13/11 there is a gap so the start and end date is* 不可能。
    • 跳转到最后一个代码块*I want to return only the datespans up to the first gap encountered.*

    首先,这里有一个虚拟表来讨论

    create table spans (date1 datetime, date2 datetime);
    insert into spans select '20110101', '20110110';
    insert into spans select '20110113', '20110115';
    insert into spans select '20110120', '20110130';
    

    这是一个单独列出日历中所有日期的查询

    declare @startdate datetime, @enddate datetime
    select @startdate = '20110107', @enddate = '20110114'
    select distinct a.date1+v.number
    from spans A
    inner join master..spt_values v
      on v.type='P' and v.number between 0 and datediff(d, a.date1, a.date2)
    -- we don't care about spans that don't intersect with our range
    where A.date1 <= @enddate
      and @startdate <= A.date2
    

    有了这个查询,我们现在可以测试看看是否有任何差距,通过 根据预期天数计算日历中的天数

    declare @startdate datetime, @enddate datetime
    select @startdate = '20110107', @enddate = '20110114'
    
    select case when count(distinct a.date1+v.number)
        = datediff(d,@startdate, @enddate) + 1
        then 'No gaps' else 'Gap' end
    from spans A
    inner join master..spt_values v
      on v.type='P' and v.number between 0 and datediff(d, a.date1, a.date2)
    -- we don't care about spans that don't intersect with our range
    where A.date1 <= @enddate
      and @startdate <= A.date2
    -- count only those dates within our range
       and a.date1 + v.number between @startdate and @enddate
    

    另一种方法是从@start 构建日历 到前面@end,看看这个日期是否有一个跨度

    declare @startdate datetime, @enddate datetime
    select @startdate = '20110107', @enddate = '20110114'
    -- startdate+v.number is a day on the calendar
    select @startdate + v.number
    from master..spt_values v
    where v.type='P' and v.number between 0
      and datediff(d, @startdate, @enddate)
    
    -- run the part above this line alone to see the calendar
    -- the condition checks for dates that are not in any span (gap)
      and not exists (
        select *
        from spans
        where @startdate + v.number between date1 and date2)
    

    查询返回日期范围@start - @end 中的所有间隔日期 可以加个TOP 1,看看有没有差距

    要返回间隙之前的所有记录,请将查询用作 更大查询中的派生表

    declare @startdate datetime, @enddate datetime
    select @startdate = '20110107', @enddate = '20110114'
    select *
    from spans
    where date1 <= @enddate and @startdate <= date2 -- overlaps
      and date2 < ( -- before the gap
        select top 1 @startdate + v.number
        from master..spt_values v
        where v.type='P' and v.number between 0
          and datediff(d, @startdate, @enddate)
          and not exists (
            select *
            from spans
            where @startdate + v.number between date1 and date2)
        order by 1 ASC
    )
    

    【讨论】:

      【解决方案2】:

      假设 MySQL,这样的事情会起作用:

      select @olddate := null;
      
      select start_date, end_date, datediff(end_date, @olddate) as diff, @olddate:=enddate
      from table
      order by start_date asc, end_date asc
      having diff > 1;
      

      基本上:将前一行的 end_date 缓存在 @olddate 变量中,然后使用 currel enddate 对该“旧”值进行比较。 having 子句将只返回两行之间的差异大于一天的记录。

      免责声明:尚未对此进行测试,但基本查询构造应该可以工作。

      【讨论】:

        【解决方案3】:

        我希望能够在 1/10/11 和 1/13/11 有一个差距,所以 开始和结束日期不是 可能的。

        我想您是在问这个问题:您表中的数据在开始日期和结束日期之间是否存在间隔?

        我创建了一个单列表 date_span,并将您的日期跨度插入其中。

        您可以通过计算开始日期和结束日期之间的天数,并比较相同范围的 date_span 中的行数来识别差距。

        select 
          date '2011-01-14' - date '2011-01-07' + 1 as elapsed_days,  
          count(*) from date_span 
        where cal_date between '2011-01-07' and '2011-01-14';
        

        返回

        elapsed_days count    
        --           --
        8            6
        

        由于它们不相等,因此在 2011-01-07 和 2011-01-14 之间的“date_span”表中存在间隔。我现在就停在那里,因为我真的不确定你想做什么。

        【讨论】:

          猜你喜欢
          • 2011-04-29
          • 1970-01-01
          • 1970-01-01
          • 2010-09-06
          • 2011-06-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-11
          • 1970-01-01
          相关资源
          最近更新 更多