使用重叠时间跨度对数据进行SQL分组

2022-04-16 00:00:00 sql common-table-expression sql-server

我需要根据记录的开始和结束时间,通过重叠的时间跨度将彼此相关的数据分组在一起。SQL-FIDDLE此处:http://sqlfiddle.com/#!18/87e4b/1/0

我构建的当前查询给出的结果不正确。CallID%3应提供的CallCount为4。这并不是因为记录6不包括(因为它不与3重叠),而是因为它与其他相关记录之一重叠而应包括在内。因此,我认为可能需要一个递归CTE,但我不确定如何编写它。

架构:

CREATE TABLE Calls
    ([callid] int, [src] varchar(10), [start] datetime, [end] datetime, [conf] varchar(5));

INSERT INTO Calls
    ([callid],[src],[start],[end],[conf])
VALUES
    ('1','5555550001','2019-07-09 10:00:00', '2019-07-09 10:10:00', '111'),
    ('2','5555550002','2019-07-09 10:00:01', '2019-07-09 10:11:00', '111'),
    ('3','5555550011','2019-07-09 11:00:00', '2019-07-09 11:10:00', '111'),
    ('4','5555550012','2019-07-09 11:00:01', '2019-07-09 11:11:00', '111'),
    ('5','5555550013','2019-07-09 11:01:00', '2019-07-09 11:15:00', '111'),
    ('6','5555550014','2019-07-09 11:12:00', '2019-07-09 11:16:00', '111'),
    ('7','5555550014','2019-07-09 15:00:00', '2019-07-09 15:01:00', '111');

当前查询:

SELECT 
    detail_record.callid,
    detail_record.conf,
    MIN(related_record.start) AS sessionStart,
    MAX(related_record.[end]) As sessionEnd,
    COUNT(related_record.callid) AS callCount
FROM    
    Calls AS detail_record
    INNER JOIN
    Calls AS related_record     
        ON related_record.conf = detail_record.conf
        AND ((related_record.start >= detail_record.start
                AND related_record.start < detail_record.[end])
            OR (related_record.[end] > detail_record.start
                AND related_record.[end] <= detail_record.[end])
            OR (related_record.start <= detail_record.start
                AND related_record.[end] >= detail_record.[end])
            )
WHERE
    detail_record.start > '1/1/2019'
    AND detail_record.conf = '111'
GROUP BY
    detail_record.callid,
    detail_record.start,
    detail_record.conf
HAVING 
    MIN(related_record.start) >= detail_record.start
ORDER BY sessionStart DESC

预期结果:

callid  conf  sessionStart          sessionEnd              callCount
   7    111   2019-07-09T15:00:00Z  2019-07-09T15:01:00Z    1
   3    111   2019-07-09T11:00:00Z  2019-07-09T11:15:00Z    4
   1    111   2019-07-09T10:00:00Z  2019-07-09T10:11:00Z    2

解决方案

这是一个缺口和孤岛问题。它不需要递归CTE。您可以使用窗口函数:

select min(callid), conf, grouping, min([start]), max([end]), count(*)
from (select c.*,
             sum(case when prev_end < [start] then 1 else 0 end) over (order by start) as grouping
      from (select c.*,
                   max([end]) over (partition by conf order by [start] rows between unbounded preceding and 1 preceding) as prev_end
            from calls c
           ) c
     ) c
group by conf, grouping;

最里面的子查询计算上一个结尾。中间的子查询将其与当前开始进行比较,以确定相邻行的组何时是新组的开始。然后,累加和确定分组。

和,外部查询聚合以汇总有关每个组的信息。

Here是小提琴。

相关文章