跨适当间隔的总持续时间

时间:2018-12-10 00:19:37

标签: sql sql-server

我需要一些帮助。我有一张个人联系表(即电话),其中包含到达时间和持续时间。我需要能够计算每个15分钟间隔内收到的联系数量,以及求和每个间隔内的“通话时间”。应答的计数将固定在到达的间隔中,持续时间的适当部分将在联系人的“时钟正在运行”的每个间隔进行计数。

例如,联系人到达2018-12-06 07:15:01.000,持续超过15分钟(947秒)。我怎样才能使900秒以7:15的间隔出现,而剩下的47秒以7:30的间隔出现?

下面是一些CTE形式的测试数据。

我已经弄清楚了15分钟的间隔部分,但是我不知道如何处理持续时间总和。我尝试了几件事,但我的大脑被卡住了。我也许最终可以弄清楚,但是我在这个项目中所采取的方向取决于我能否迅速获得成功。我知道这是可能的,因为源GUI上的内置报表查看器可以做到这一点。

如果能获得任何帮助,我将不胜感激。

;with cteTestData as (
SELECT 1 as ID, '2018-12-06 07:03:27.000' as ContactStartTime, Sum(Left('00:04:28',2) * 3600 + substring('00:04:28', 4,2) * 60 + substring('00:04:28', 7,2)) as Duration
UNION ALL
SELECT 2 as ID, '2018-12-06 07:03:32.000' as ContactStartTime, Sum(Left('00:14:28',2) * 3600 + substring('00:14:28', 4,2) * 60 + substring('00:14:28', 7,2)) as Duration
UNION ALL
SELECT 3 as ID, '2018-12-06 07:08:12.000' as ContactStartTime, Sum(Left('00:10:03',2) * 3600 + substring('00:10:03', 4,2) * 60 + substring('00:10:03', 7,2)) as Duration
UNION ALL
SELECT 4 as ID, '2018-12-06 07:14:59.000' as ContactStartTime, Sum(Left('00:02:58',2) * 3600 + substring('00:02:58', 4,2) * 60 + substring('00:02:58', 7,2)) as Duration
UNION ALL
SELECT 5 as ID, '2018-12-06 07:15:01.000' as ContactStartTime, Sum(Left('00:15:47',2) * 3600 + substring('00:15:47', 4,2) * 60 + substring('00:15:47', 7,2)) as Duration
UNION ALL
SELECT 6 as ID, '2018-12-06 07:15:12.000' as ContactStartTime, Sum(Left('00:08:18',2) * 3600 + substring('00:08:18', 4,2) * 60 + substring('00:08:18', 7,2)) as Duration
UNION ALL
SELECT 7 as ID, '2018-12-06 07:18:50.000' as ContactStartTime, Sum(Left('00:10:22',2) * 3600 + substring('00:10:22', 4,2) * 60 + substring('00:10:22', 7,2)) as Duration
UNION ALL
SELECT 8 as ID, '2018-12-06 07:20:05.000' as ContactStartTime, Sum(Left('00:03:11',2) * 3600 + substring('00:03:11', 4,2) * 60 + substring('00:03:11', 7,2)) as Duration
UNION ALL
SELECT 9 as ID, '2018-12-06 07:29:32.000' as ContactStartTime, Sum(Left('00:32:53',2) * 3600 + substring('00:32:53', 4,2) * 60 + substring('00:32:53', 7,2)) as Duration
UNION ALL
SELECT 10 as ID, '2018-12-06 07:35:17.000' as ContactStartTime, Sum(Left('00:07:37',2) * 3600 + substring('00:07:37', 4,2) * 60 + substring('00:07:37', 7,2)) as Duration
)
select ID, ContactStartTime
, DATEADD(minute, (DATEDIFF( minute, 0, ContactStartTime) / 15) * 15, 0) AS ContactStartInterval
, Duration

FROM cteTestData
ORDER BY ContactStartTime

编辑:这是Squirrel的更新查询,其中添加了注释。

    ;with 
cteTestData   /*this will not be necessary in the production query, as this CTE will be replaced with the actual source table*/
as 
(
    SELECT 1 as ID, '2018-12-06 07:03:27.000' as ContactStartTime, (Left('00:04:28',2) * 3600 + substring('00:04:28', 4,2) * 60 + substring('00:04:28', 7,2)) as Duration
    UNION ALL
    SELECT 2 as ID, '2018-12-06 07:03:32.000' as ContactStartTime, (Left('00:14:28',2) * 3600 + substring('00:14:28', 4,2) * 60 + substring('00:14:28', 7,2)) as Duration
    UNION ALL
    SELECT 3 as ID, '2018-12-06 07:08:12.000' as ContactStartTime, (Left('00:10:03',2) * 3600 + substring('00:10:03', 4,2) * 60 + substring('00:10:03', 7,2)) as Duration
    UNION ALL
    SELECT 4 as ID, '2018-12-06 07:14:59.000' as ContactStartTime, (Left('00:02:58',2) * 3600 + substring('00:02:58', 4,2) * 60 + substring('00:02:58', 7,2)) as Duration
    UNION ALL
    SELECT 5 as ID, '2018-12-06 07:15:01.000' as ContactStartTime, (Left('00:15:47',2) * 3600 + substring('00:15:47', 4,2) * 60 + substring('00:15:47', 7,2)) as Duration
    UNION ALL
    SELECT 6 as ID, '2018-12-06 07:15:12.000' as ContactStartTime, (Left('00:08:18',2) * 3600 + substring('00:08:18', 4,2) * 60 + substring('00:08:18', 7,2)) as Duration
    UNION ALL
    SELECT 7 as ID, '2018-12-06 07:18:50.000' as ContactStartTime, (Left('00:10:22',2) * 3600 + substring('00:10:22', 4,2) * 60 + substring('00:10:22', 7,2)) as Duration
    UNION ALL
    SELECT 8 as ID, '2018-12-06 07:20:05.000' as ContactStartTime, (Left('00:03:11',2) * 3600 + substring('00:03:11', 4,2) * 60 + substring('00:03:11', 7,2)) as Duration
    UNION ALL
    SELECT 9 as ID, '2018-12-06 07:29:32.000' as ContactStartTime, (Left('00:32:53',2) * 3600 + substring('00:32:53', 4,2) * 60 + substring('00:32:53', 7,2)) as Duration
    UNION ALL
    SELECT 10 as ID, '2018-12-06 07:35:17.000' as ContactStartTime, (Left('00:07:37',2) * 3600 + substring('00:07:37', 4,2) * 60 + substring('00:07:37', 7,2)) as Duration
),
cteIntervalNumbers as /*tally table to generate a number for each interval*/
(
    SELECT  num = 1
    union all
    SELECT  num = num + 1
    FROM    cteIntervalNumbers
    WHERE   num < 99
)
,
cteTimes as /*CTE to calculate ContactEndTime, ContactStartInterval, ContactEndInterval*/
(
    SELECT  ID, ContactStartTime
          , Duration
          , DATEADD(second, Duration, ContactStartTime) AS ContactEndTime
          , DATEADD(minute, (DATEDIFF( minute, 0, ContactStartTime) / 15) * 15, 0) AS ContactStartInterval
          , DATEADD(minute, (DATEDIFF( minute, 0, DATEADD(second, Duration, ContactStartTime)) / 15) * 15 + 15, 0) AS ContactEndInterval
    FROM    cteTestData /*this will be the source table*/
)
SELECT IntervalStart, count(DISTINCT ID) as Contacts, SUM(DurationInterval) as TalkTime
FROM (
SELECT  *
        ,
        CASE    
        /*all of the time exists in the interval; just find the difference in start and end, which will be the entire duration*/
        WHEN  ContactStartTime >= IntervalStart AND ContactStartTime < IntervalEnd
        AND   ContactEndTime >= IntervalStart AND ContactEndTime < IntervalEnd
        THEN  DATEDIFF(second, ContactStartTime, ContactEndTime)
        /*contact carries over into next interval; get time between the start time and the end of that interval*/
        WHEN  ContactStartTime >= IntervalStart AND ContactStartTime < IntervalEnd
        AND   ContactEndTime > IntervalEnd
        THEN  DATEDIFF(second, ContactStartTime, IntervalEnd)
        /*this will get the elapsed time in the carry over intervals where the contact ends within that interval*/
        WHEN  ContactStartTime < IntervalStart 
        AND   ContactEndTime >= IntervalStart  AND ContactEndTime < IntervalEnd
        THEN  DATEDIFF(second, IntervalStart, ContactEndTime)
        /*this is for all intervals where the contact neither starts nor ends i.e. where the full elapsed time of the interval is needed*/
        ELSE  DATEDIFF(second, IntervalStart, IntervalEnd)
        END AS DurationInterval
FROM    cteTimes d
        CROSS JOIN cteIntervalNumbers n
        CROSS APPLY /*this calcualtes the start and end time for each interval that the contact crossed*/
        (
            SELECT   DATEADD(minute, (num - 1) * 15, ContactStartInterval) AS IntervalStart
                    ,DATEADD(minute, num * 15, ContactStartInterval) AS IntervalEnd
        ) i
/*only show intervals crossed by each contact*/
WHERE   n.num <= datediff(minute, ContactStartInterval, ContactEndInterval) / 15 /*how many intervals does the contact cross?*/
) rawdata
GROUP BY IntervalStart

1 个答案:

答案 0 :(得分:0)

请参考查询中的注释进行解释。

基本上,它会检查间隔内有多少时间

|--------|---------|--------|     interval
    S...E                         [1]
    S....                         [2]
          ....E                   [3]
          .........               [4]


;with 
cteTestData   -- Your Sample Data
as 
(
    SELECT 1 as ID, '2018-12-06 07:03:27.000' as ContactStartTime, (Left('00:04:28',2) * 3600 + substring('00:04:28', 4,2) * 60 + substring('00:04:28', 7,2)) as Duration
    UNION ALL
    SELECT 2 as ID, '2018-12-06 07:03:32.000' as ContactStartTime, (Left('00:14:28',2) * 3600 + substring('00:14:28', 4,2) * 60 + substring('00:14:28', 7,2)) as Duration
    UNION ALL
    SELECT 3 as ID, '2018-12-06 07:08:12.000' as ContactStartTime, (Left('00:10:03',2) * 3600 + substring('00:10:03', 4,2) * 60 + substring('00:10:03', 7,2)) as Duration
    UNION ALL
    SELECT 4 as ID, '2018-12-06 07:14:59.000' as ContactStartTime, (Left('00:02:58',2) * 3600 + substring('00:02:58', 4,2) * 60 + substring('00:02:58', 7,2)) as Duration
    UNION ALL
    SELECT 5 as ID, '2018-12-06 07:15:01.000' as ContactStartTime, (Left('00:15:47',2) * 3600 + substring('00:15:47', 4,2) * 60 + substring('00:15:47', 7,2)) as Duration
    UNION ALL
    SELECT 6 as ID, '2018-12-06 07:15:12.000' as ContactStartTime, (Left('00:08:18',2) * 3600 + substring('00:08:18', 4,2) * 60 + substring('00:08:18', 7,2)) as Duration
    UNION ALL
    SELECT 7 as ID, '2018-12-06 07:18:50.000' as ContactStartTime, (Left('00:10:22',2) * 3600 + substring('00:10:22', 4,2) * 60 + substring('00:10:22', 7,2)) as Duration
    UNION ALL
    SELECT 8 as ID, '2018-12-06 07:20:05.000' as ContactStartTime, (Left('00:03:11',2) * 3600 + substring('00:03:11', 4,2) * 60 + substring('00:03:11', 7,2)) as Duration
    UNION ALL
    SELECT 9 as ID, '2018-12-06 07:29:32.000' as ContactStartTime, (Left('00:32:53',2) * 3600 + substring('00:32:53', 4,2) * 60 + substring('00:32:53', 7,2)) as Duration
    UNION ALL
    SELECT 10 as ID, '2018-12-06 07:35:17.000' as ContactStartTime, (Left('00:07:37',2) * 3600 + substring('00:07:37', 4,2) * 60 + substring('00:07:37', 7,2)) as Duration
),
numbers as       -- recursive CTE as number / tally table
(
    SELECT  n = 1
    union all
    SELECT  n = n + 1
    FROM    numbers
    WHERE   n < 99
),
cte as          -- CTE for calculate ContactEndTime, ContactStartInterval, ContactEndInterval
(
    SELECT  ID, ContactStartTime
          , Duration
          , DATEADD(second, Duration, ContactStartTime) AS ContactEndTime
          , DATEADD(minute, (DATEDIFF( minute, 0, ContactStartTime) / 15) * 15, 0) AS ContactStartInterval
          , DATEADD(minute, (DATEDIFF( minute, 0, DATEADD(second, Duration, ContactStartTime)) / 15) * 15 + 15, 0) AS ContactEndInterval
    FROM    cteTestData
)
-- the main query. 
SELECT  *,
        CASE    
        -- [1]
        WHEN  ContactStartTime  >= IntervalStart    AND ContactStartTime    < IntervalEnd
        AND   ContactEndTime    >= IntervalStart    AND ContactEndTime      < IntervalEnd
        THEN  DATEDIFF( second, ContactStartTime, ContactEndTime)
        -- [2]
        WHEN  ContactStartTime  >= IntervalStart    AND ContactStartTime    < IntervalEnd
        AND   ContactEndTime    > IntervalEnd
        THEN  DATEDIFF( second, ContactStartTime, IntervalEnd)
        -- [3]
        WHEN  ContactStartTime  < IntervalStart 
        AND   ContactEndTime    >= IntervalStart    AND ContactEndTime      < IntervalEnd
        THEN  DATEDIFF( second, IntervalStart, ContactEndTime)
        -- [4]
        ELSE  DATEDIFF( second, IntervalStart, IntervalEnd)
        END AS DurationInterval
FROM    cte d
        CROSS JOIN numbers n
        CROSS APPLY  -- this calcualtes the start and end time for each interval
        (
            SELECT   DATEADD( minute, (n - 1) * 15, ContactStartInterval) AS IntervalStart
                    ,DATEADD( minute, n * 15, ContactStartInterval) AS IntervalEnd
        ) i
WHERE   n.n <= datediff(minute, ContactStartInterval, ContactEndInterval) / 15
ORDER BY ContactStartTime, n