如何检查一组行是否覆盖整个时间范围

How to check if a set rows is covering the whole time range

我有一个 table,其中有两列 StartTime 和 EndTime。值填充为:

 declare @tbl Table(
colA VARCHAR(50),
colS VARCHAR(50),
DATES DATE,
STARTTIME TIME,
ENDTIME TIME,
ID BIGINT NOT NULL IDENTITY(1,1)
)

INSERT INTO @tbl
SELECT 'A','S',convert(date,'2015-09-21'),convert(TIME,'12:45'),convert(TIME,'13:30')
UNION ALL
SELECT 'A','S',convert(date,'2015-09-21'),convert(TIME,'13:15'),convert(TIME,'13:45')
UNION ALL
SELECT 'A','S',convert(date,'2015-09-21'),convert(TIME,'13:30'),convert(TIME,'16:50')
UNION ALL
SELECT 'A','S',convert(date,'2015-09-21'),convert(TIME,'13:15'),convert(TIME,'13:50')

因此,我想检查这个table的所有行中的StartTime和EndTime是否涵盖了这个table的Minimum StartTime和Maximum EndTime之间的整个时间段。 因此,从这些行可以清楚地看出这是真的。但是对于下面的一组行,由于它们之间的时间间隔,它不会是真的。所以我只想要 True 或 False 作为结果。

INSERT INTO @tbl
SELECT 'A','S',convert(date,'2015-09-21'),convert(TIME,'08:45'),convert(TIME,'09:15')
UNION ALL
SELECT 'A','S',convert(date,'2015-09-21'),convert(TIME,'11:10'),convert(TIME,'11:25')

我已经设法完成了所有其他任务,这就是它们的最终结果。这是最后的任务,我完全不知道该怎么做。任何帮助将不胜感激。

尝试

;with a as (select *,row_number() over (order by DATES,starttime) rn from @tbl)

select a.*,
case when isnull(b.endtime,a.endtime)>=a.STARTTIME then 'true' else 'false' end 
 from a left join a b on a.rn=b.rn+1

如果足以检查下一行的时间间隔是否与下一行间隔相交,那么 OUTER APPLY 应该为您做一些事情:

SELECT  t.*, 
        CASE WHEN t.ENDTIME between p.STARTTIME and p.ENDTIME OR p.ENDTIME IS NULL THEN 'TRUE' ELSE 'FALSE' END as Seq
FROM @tbl t
OUTER APPLY (
    SELECT TOP 1 * 
    FROM @tbl 
    WHERE t.STARTTIME < STARTTIME and t.colA = colA and t.colS = colS and t.DATES = DATES
    ORDER BY STARTTIME ASC) p
ORDER BY t.STARTTIME

给定数据的输出:

colA    colS    DATES       STARTTIME           ENDTIME             ID  Seq
A       S       2015-09-21  08:45:00.0000000    09:15:00.0000000    5   FALSE
A       S       2015-09-21  11:10:00.0000000    11:25:00.0000000    6   FALSE
A       S       2015-09-21  12:45:00.0000000    13:30:00.0000000    1   TRUE
A       S       2015-09-21  13:15:00.0000000    13:45:00.0000000    2   TRUE
A       S       2015-09-21  13:15:00.0000000    13:50:00.0000000    4   TRUE
A       S       2015-09-21  13:30:00.0000000    16:50:00.0000000    3   TRUE

-- 第一个示例数据的最后一列为 1,第二个示例数据为 0。

    SELECT t.*, 
    CASE WHEN t.ENDTIME < m.MaxStartTime THEN 0 
         WHEN t.STARTTIME > m.MinEndTime THEN 0 
         ELSE 1 END AS Covered
    FROM @tbl AS t
    JOIN 
         (SELECT DATES,
                 MIN(ENDTIME) MinEndTime,
                 MAX(STARTTIME) MaxStartTime
                 FROM @tbl
                 GROUP BY DATES) AS m
    ON t.DATES = m.DATES

通常间隔可以是任意长度,因此比较相邻行远非完整的解决方案。除了上述评论中引用的 Itzik Ben-Gan's Packing Intervals 解决方案外,另一个完整的解决方案是每分钟(或其他粒度)检查一次。

select uncoverdMinutes=count(*) -- 0 is true
from(
    select colA, colS, t.m
    from ( 
        select colA, colS
          , mst = min(starttime)
          , cnt = datediff(minute, min(starttime), max(endtime)) + 1
        from @tbl
        group by colA, colS 
    ) prm 
    cross apply (
        select top(prm.cnt)
            m = dateadd(minute
                      ,row_number() over(order by (select null)) - 1
                      ,prm.mst)                                    
        from sys.all_objects -- use tally table instead, if you have one
        ) t
  ) mi
where not exists (
    select 1 
    from @tbl t
    where mi.m between t.starttime and t.endtime)