Данные группировки SQL с перекрывающимися временными интервалами - PullRequest
2 голосов
/ 09 июля 2019

Мне нужно сгруппировать данные, которые связаны друг с другом, перекрывая временные диапазоны на основе времени начала и окончания записи. SQL-скрипка здесь: 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

1 Ответ

2 голосов
/ 10 июля 2019

Это проблема пробелов и островков. Не требует рекурсивного 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;

Самый внутренний подзапрос вычисляет предыдущий конец. Средний подзапрос сравнивает это с текущим началом, чтобы определить, когда группы смежных строк являются началом новой группы. Затем совокупная сумма определяет группировку.

И внешний запрос агрегирует для суммирования информации о каждой группе.

Здесь - это дБ <> скрипка.

...