【问题标题】:How to create segments from time periods如何从时间段创建细分
【发布时间】:2019-07-11 01:28:23
【问题描述】:

我有 2 种活动类型(任务、聊天)。每种活动类型都有一个优先级,其中 10 是最高优先级。

我有每个活动的开始时间和结束时间。

我需要确定代理在任何给定时间都在积极开展哪些活动。如果 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 在 CHAT 之前和之后都有一个活动段。

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   |
+------------------+------------------+----------+

【问题讨论】:

  • 活动的开始和结束时间总是以 15 分钟为单位吗?如果没有,那么在预期输出中将如何显示?
  • 嗨@Squirrel 它并不总是以 15 分钟为间隔。这将在预期输出中显示,类似于上面的输出,但是分钟将显示相应的开始/结束时间。例如 09:01 开始和 09:11 结束,然后是 09:11 开始和 10:27 结束(如果这是活动时间的话)。
  • 根据您的上一个示例,预期结果如何?您可以在示例数据和预期结果中更新它吗?
  • 嗨@Squirrel 为了简单起见,我在示例数据中使用了 15 分钟的间隔,但是活动的开始和结束时间可以随时发生,因为它取决于代理开始/停止活动。
  • @J.Bradow 第二种情况——为什么聊天时间从15 to 30扩大到15 to 45

标签: sql sql-server tsql sql-server-2016


【解决方案1】:

当我们最多有两个重叠周期时,以下是有效的(如果您需要更多,您需要更改代码以使用递归公用表表达式)。

想法是使用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

【讨论】:

  • 嗨@gotqn 我已经用以下数据对此进行了测试,但未能返回预期结果:('TASK', 7, '2019/07/01 09:00', '2019/07 /01 10:00') ('CHAT', 10, '2019/07/01 09:00', '2019/07/01 09:45')
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-26
  • 2023-03-23
  • 1970-01-01
  • 2015-08-16
  • 1970-01-01
  • 2018-12-13
  • 1970-01-01
相关资源
最近更新 更多