TSQL:将连续时隙分组在一起

时间:2019-03-22 07:23:40

标签: tsql

我必须将连续的时隙分组在一起:

示例:

DECLARE @TEST as Table (ID int, tFrom datetime, tUntil dateTime)
insert into @TEST Values (1,'2019-1-1 12:00', '2019-1-1 13:00')
insert into @TEST Values (1,'2019-1-1 13:00', '2019-1-1 14:00')
insert into @TEST Values (1,'2019-1-1 14:00', '2019-1-1 16:00')
insert into @TEST Values (1,'2019-1-1 18:00', '2019-1-1 19:00')
insert into @TEST Values (1,'2019-1-1 19:00', '2019-1-1 20:00')
insert into @TEST Values (1,'2019-1-1 20:00', '2019-1-1 21:00')
insert into @TEST Values (1,'2019-1-1 22:00', '2019-1-1 23:00')
insert into @TEST Values (2,'2019-1-1 12:00', '2019-1-1 13:00')
insert into @TEST Values (2,'2019-1-1 13:00', '2019-1-1 14:00')
insert into @TEST Values (2,'2019-1-1 14:00', '2019-1-1 16:00')
insert into @TEST Values (2,'2019-1-1 18:00', '2019-1-1 19:00')
insert into @TEST Values (2,'2019-1-1 19:00', '2019-1-1 20:00')
insert into @TEST Values (2,'2019-1-1 20:00', '2019-1-1 21:00')
insert into @TEST Values (2,'2019-1-1 22:00', '2019-1-1 23:00')

预期结果:

1; 2019-1-1 12:00; 2019-1-1 16:00
1; 2019-1-1 18:00; 2019-1-1 21:00
1; 2019-1-1 22:00; 2019-1-1 23:00
2; 2019-1-1 12:00; 2019-1-1 16:00
2; 2019-1-1 18:00; 2019-1-1 21:00
2; 2019-1-1 22:00; 2019-1-1 23:00

2 个答案:

答案 0 :(得分:0)

这是分类差距和岛屿问题。 这里的关键是如何识别组。

如果tFromtUntil之间的差异始终恰好是一个小时,则可以忽略tUntil并仅根据不同记录的tFrom之间的差异进行工作。
使用公用表表达式来标识组,然后从中选择min(tFrom)max(tUntil)(按ID和组分组)。

您要做的是计算tFrom与某个固定日期之间的时差,然后从row_number排序的tFrom中减去该值(并按id进行分区在这种情况下)。

这意味着tFrom的连续值将获得相同的组密钥(在这种情况下,此处连续表示小时):

WITH CTE AS
(
    SELECT  ID, 
            tFrom, 
            tUntil,
            ROW_NUMBER() OVER(PARTITION BY id  ORDER BY tFrom) - 
            DATEDIFF(HOUR, '2019-01-01', tFrom) As grp
    FROM @Test
)

SELECT  ID, 
        MIN(tFrom) As tFrom, 
        MAX(tUntil) As tUntil         
FROM CTE
GROUP BY ID, grp
ORDER BY Id, tFrom

如果tFromtUntill之间的差异不固定,那么识别组将变得更加麻烦。
我想出了一个涉及三个常用表表达式的解决方案-第一个是获取当前行的tUntill与下一行的tFrom之间的datediff,然后根据上一行的差计算组分隔符,然后然后根据除法器的总和计算组ID:

WITH CTE1 AS
(
    SELECT  ID, 
            tFrom, 
            tUntil,
            DATEDIFF(HOUR, tUntil, LEAD(tFrom) OVER(PARTITION BY id  ORDER BY tFrom)) As DiffNext
    FROM @Test
), CTE2 AS
(
    SELECT  ID, 
            tFrom, 
            tUntil,
            ISNULL(SIGN(LAG(DiffNext) OVER(PARTITION BY id  ORDER BY tFrom)), 1) AS GroupDivider
    FROM CTE1
), CTE3 AS
(
    SELECT  ID, 
            tFrom, 
            tUntil,
            SUM(GroupDivider) OVER(PARTITION BY id  ORDER BY tFrom) As GroupId
    FROM CTE2
)

SELECT  ID, 
        MIN(tFrom) As tFrom, 
        MAX(tUntil) As tUntil
FROM CTE3
GROUP BY ID, GroupId
ORDER BY ID, tFrom

答案 1 :(得分:0)

美好的一天,

为了有一个灵活的解决方案来覆盖时间范围内的重叠,我们可以使用几种解决方案。从性能的角度来看,“ 间隙和孤岛”方法不是最好的方法,但是它会起作用,并且还有更差的选择(例如使用循环/光标)。由于“空白与离岛”是评论中以及在评论中讨论的解决方案中提到的短语,因此我将首先简短地展示此解决方案。

使用“空白和离岛”方法的解决方案基于两个步骤(一个查询使用CTE)。首先,将范围划分为“时间点”。接下来,使用“数字”表或在这种情况下更好的是“时间”表,您可以通过找到两点之间的间隙来获得最终结果SET,这是经典的“间隙和孤岛”问题。

我强烈建议follow the post, which I published,并从头到尾遵循它!您必须了解这种方法的局限性和缺点。此外,该帖子还介绍了“思维方式”以及我们如何逐步解决此类问题。

在本文中,我以最简单的情况为例,即整数范围,例如2-4、6-8、8-10、13-14,应将其分组为2-4, 6-10,13-14。

接下来,我将解释与范围之间的空间分辨率有关的问题,并为十进制数字的范围提供解决方案,以解决问题。

最后,使用我为INTEGERS详细介绍的解决方案,我提出了“将连续的时隙分组在一起”的解决方案,这是论坛中的原始问题。

注意!这里介绍的解决方案可能是我建议在生产中使用的解决方案。在我的下一篇文章中,我使用个人技巧发布了一种完全不同的方法,可以显着提高性能。

简而言之,为了便于讨论,我将创建一个Times表(如果需要,可以直接使用Numbers表)。注意,我使用Numbers表创建Times表。

DROP TABLE IF EXISTS Times
GO
SELECT DT = DATEADD(MINUTE, N*10, '2010-01-01')
    INTO Times
FROM Numbers
GO
CREATE CLUSTERED INDEX IX_DT ON Times(DT)
GO
SELECT TOP 1000 DT from Times
GO

并使用此表可以解决问题

;With MyCTE01 as (
    SELECT DISTINCT ID, DT
    FROM TEST t
    INNER JOIN Times dt ON DT between tFrom and tUntil
)
,MyCTE02 as(
    SELECT ID, DT,
        MyGroup =
            DATEDIFF(MINUTE,
                DATEADD(MINUTE, 10 * ROW_NUMBER()OVER(PARTITION BY ID ORDER BY ID,DT),0),
                DT
            )
    from MyCTE01
    --order by ID,DT
)
SELECT ID, MIN(DT) tFrom, MAX(DT) tUntil
FROM MyCTE02
GROUP BY ID, MyGroup
ORDER BY ID, tFrom
GO

注意!在选择适合您生产的解决方案之前,我高度recommend to check the second post(第2部分)。

我希望本文涵盖了讨论并且对我们有帮助