Как создать сегменты из периодов времени - PullRequest
2 голосов
/ 11 июля 2019

У меня есть 2 вида деятельности (ЗАДАЧА, ЧАТ).Каждый тип действия имеет приоритет, причем наивысшим приоритетом является 10.

У меня есть StartTime и EndTime каждого действия.

Мне нужно определить, над какой операцией агент активно работает в любой данный момент.время.Там, где перекрываются 2 действия, считается, что это работа с наивысшим приоритетом, над которой работает агент.

Мои первые мысли о решении этой проблемы состоят в том, чтобы разбить время активности на сегменты, основанные на StartTime и EndTimeа также время наложения деятельности.Затем для каждого сегмента выполняется действие с наивысшим приоритетом.

Например: 09: 00-09: 15 (ЧАТ), 09: 15-09: 30 (ЧАТ), 09: 30-09: 45(ЗАДАЧА)

Однако я действительно не представляю, как добиться этого в SQL.Я провел некоторое исследование «Пробелов и островов», но я не нашел примеров, которые я мог бы перевести на решение этой проблемы.

Мне интересно, сможет ли кто-нибудь дать мне совет относительно лучшегоподход к этой проблеме и возможное решение, чтобы я мог понять решение и реализовать его?

МОИ ДАННЫЕ

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') INSERT INTO @ActivityLog VALUES ('TASK', 5, '2019/07/01 09:15', '2019/07/01 09:45')

SELECT * FROM @ActivityLog

Ожидаемый конечный результат

+------------------+------------------+----------+
|     StartTime    |      EndTime     | Activity |
+------------------+------------------+----------+
| 2019/07/01 09:00 | 2019/07/01 09:15 |   CHAT   |
+------------------+------------------+----------+
| 2019/07/01 09:15 | 2019/07/01 09:30 |   CHAT   |
+------------------+------------------+----------+
| 2019/07/01 09:30 | 2019/07/01 09:45 |   TASK   |
+------------------+------------------+----------+

Сценарий 2 TASK имеет активный сегмент до и после ЧАТ.

DECLARE @ActivityLog TABLE (Activity VARCHAR(8), Priority INT, StartTime DATETIME, EndTime DATETIME)

INSERT INTO @ActivityLog VALUES ('TASK', 7, '2019/07/01 09:00', '2019/07/01 10:00')
INSERT INTO @ActivityLog VALUES ('CHAT', 10, '2019/07/01 09:15', '2019/07/01 09:45');

Сценарий 2 Ожидаемый результат

+------------------+------------------+----------+
|     StartTime    |      EndTime     | Activity |
+------------------+------------------+----------+
| 2019/07/01 09:00 | 2019/07/01 09:15 |   TASK   |
+------------------+------------------+----------+
| 2019/07/01 09:15 | 2019/07/01 09:45 |   CHAT   |
+------------------+------------------+----------+
| 2019/07/01 09:45 | 2019/07/01 10:00 |   TASK   |
+------------------+------------------+----------+

1 Ответ

1 голос
/ 11 июля 2019

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

Идея состоит в том, чтобы получить перекрытия, используя 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
...