如何检查一组行是否覆盖整个时间范围
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)
我有一个 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)