Следующее работает, когда у нас есть максимум два перекрывающихся периода (если вам нужно больше, вам нужно изменить код для использования рекурсивного выражения общей таблицы).
Идея состоит в том, чтобы получить перекрытия, используя LEFT JOIN
, а затем применить другую логику в зависимости от приоритета элементов.
Это просто, но безобразно. Я добавил еще несколько кейсов и тестов и надеюсь, что ничего не пропустил.
DECLARE @ActivityLog TABLE (Activity VARCHAR(4), Priority INT, StartTime DATETIME, EndTime DATETIME)
INSERT INTO @ActivityLog
VALUES ('CHAT', 10, '2019/07/01 09:00', '2019/07/01 09:30')
,('TASK', 5, '2019/07/01 09:15', '2019/07/01 09:20')
--
,('CHAT', 8, '2019/07/01 19:30', '2019/07/01 20:30')
--
,('TASK', 7, '2019/07/02 09:00', '2019/07/02 10:00')
,('CHAT', 10, '2019/07/02 09:15', '2019/07/02 09:30')
--
,('CHAT', 10, '2019/12/01 09:00', '2019/12/01 09:30')
,('TASK', 5, '2019/12/01 09:15', '2019/12/01 09:20');
WITH DataSourceWithRowID AS
(
SELECT *
,ROW_NUMBER() OVER (ORDER BY [StartTime]) AS [row_id]
FROM @ActivityLog
),
DataSource AS
(
SELECT S.[Activity] AS [S_Activity], S.[Priority] AS [S_Priority], S.[StartTime] AS [S_StartTime], S.[EndTime] AS [S_EndTime], S.[row_id] AS [S_row_id]
,E.[Activity] AS [E_Activity], E.[Priority] AS [E_Priority], E.[StartTime] AS [E_StartTime], E.[EndTime] AS [E_EndTime], E.[row_id] AS [E_row_id]
FROM DataSourceWithRowID S
LEFT JOIN DataSourceWithRowID E
ON E.[StartTime] > S.[StartTime]
AND E.[StartTime] < S.[EndTime]
)
-- case 1 - handle 1
SELECT [S_Activity], [S_Priority], [S_StartTime], [E_StartTime]
FROM DataSource
WHERE S_Priority > E_Priority
AND S_EndTime < E_EndTime
UNION ALL
-- case 1 - handle 2
SELECT [S_Activity], [S_Priority], [E_StartTime], [S_EndTime]
FROM DataSource
WHERE S_Priority > E_Priority
AND S_EndTime < E_EndTime
UNION ALL
-- case 1 - handle 3
SELECT [E_Activity], [E_Priority], [S_EndTime], [E_EndTime]
FROM DataSource
WHERE S_Priority > E_Priority
AND S_EndTime < E_EndTime
UNION ALL
-- case 1 - handle 4 -- sub period with low priorty -> consume by parent
SELECT [S_Activity], [S_Priority], [S_StartTime], [S_EndTime]
FROM DataSource
WHERE S_Priority > E_Priority
AND S_EndTime > E_EndTime
UNION ALL
-- case 2 - no overlapping
SELECT [S_Activity], [S_Priority], [S_StartTime], [S_StartTime]
FROM DataSource
WHERE [S_row_id] NOT IN (SELECT [E_row_id] FROM DataSource WHERE [E_row_id] IS NOT NULL)
AND [E_row_id] IS NULL
UNION ALL
-- case 3 - handle 1
SELECT S_Activity, S_Priority, S_StartTime, E_StartTime
FROM DataSource
WHERE S_Priority < E_Priority
UNION ALL
-- case 3 - handle 2
SELECT E_Activity
,E_Priority
,E_StartTime
,IIF(S_EndTime > E_EndTime, E_EndTime, S_EndTime)
FROM DataSource
WHERE S_Priority < E_Priority
UNION ALL
-- case 3 - handle 3
SELECT IIF(S_EndTime > E_EndTime, S_Activity, E_Activity)
,IIF(S_EndTime > E_EndTime, S_Priority, E_Priority)
,IIF(S_EndTime > E_EndTime, E_EndTime, S_EndTime)
,IIF(S_EndTime > E_EndTime, S_EndTime, E_EndTime)
FROM DataSource
WHERE S_Priority < E_Priority