在子查询中检索当前行和下一行之间的Timediff

时间:2015-01-30 20:13:17

标签: mysql

为什么我超过24小时?如果timediff大于10分钟,我试图在子查询中的每一行之间获得timediff。然后每天总结结果。

我的目标是为每个用户计算超过10分钟的每个制动器的总数。并列出该特定日期的通话金额?

  SELECT DATE_FORMAT(last_call, '%d, %W') AS DAY
       , COUNT(call_id) AS calls
       , ( SELECT SEC_TO_TIME(SUM((
                      SELECT timestampdiff(SECOND, c.last_call, c2.last_call)
                         FROM calls c2
                         WHERE c2.calling_agent = c.calling_agent
                           AND c2.last_call > c.last_call
                           AND timestampdiff(SECOND, c.last_call, c2.last_call) > 600
                         ORDER BY c2.last_call LIMIT 1
                  )))
             FROM calls AS c
            WHERE EXTRACT(DAY FROM c.last_call) = EXTRACT(DAY FROM calls.last_call)
         ) AS `brakes`
    FROM calls
   WHERE 9 IN (calls.reg_calling_agent)
     AND last_call > DATE_SUB(now() , INTERVAL 12 MONTH)
   GROUP BY EXTRACT(DAY FROM last_call)
   ORDER BY EXTRACT(DAY FROM last_call) DESC

1 个答案:

答案 0 :(得分:1)

你得到的时间超过24小时,因为

1)从c2检索的行可能来自不同的日期。在长达一周的假期之后,下一个电话(前一个电话后10分钟)不能保证代理人第一次拨打电话。

2)相同的"差距"代理人发出/收到的最后一次电话会报告超过10分钟。而且你也会得到一个"差距"在紧接缺口之前的代理人之前进行的呼叫与之前的代理之间的呼叫之间。也就是说,没有规定排除DID在10分钟内进行后续呼叫的呼叫。 (子查询只是在寻找呼叫后10分钟的任何后续呼叫。)

3)无论代理人是什么,你都会得到所有这些差距的总和(SUM);所有代理商的所有差距都在计算中。

4)外部查询获得了多年的呼叫(对于所有座席?),但是按月(1到31)分组。所以,你在本月的第5天回来了一行,但是会有多个代理人和多个"天" (1月5日,2月5日,3月5日等),'brakes'的多个值,并且这些值中只有一个将包含在结果中。它将返回哪些行值是不确定的。 (其他RDBMS对这个构造不了解,SELECT列表中的非聚合表达式不包含在GROUP BY中,但默认情况下,MySQL允许它。)

-

<强>后续

问:您能否发布更正的查询?

答:我没有表格架构,示例数据或规范,因此我无法提供更正的&#34;&#34 ;查询。

例如,它根本不清楚为什么在最外层查询中reg_calling_agent上有一个谓词,但是子查询没有对该列的任何引用,或任何外部查询中表中的其他列,last_call列除外。查找后续呼叫的查询依赖于calling_agent列,而不是reg_calling_agent,但是在一个月的某一天,所有呼叫正在执行此操作。

我可以拍摄一个可能更接近您所寻找的查询,但绝对无保证这是&#34;正确&#34;在匹配模式,数据类型,实际数据或预期输出方面。返回意外结果的查询是适当的规范。

SELECT a.calling_agent
     , DATE_FORMAT(a.last_call,'%d, %W') AS `day`
     , COUNT(a.call_id) AS `calls`
     , SEC_TO_TIME(
         SUM(
           SELECT IF(TIMESTAMPDIFF(SECOND, a.last_call, c.last_call) > 600
                    ,TIMESTAMPDIFF(SECOND, a.last_call, c.last_call)
                    ,NULL
                  ) AS `gap`
             FROM calls c
            WHERE c.calling_agent = a.calling_agent
              AND c.last_call     > a.last_call
              AND c.last_call     < DATE(a.last_call)+INTERVAL 1 DAY
            ORDER BY c.last_call
            LIMIT 1
         )
       ) AS `breaks`
  FROM calls a
 WHERE a.reg_calling_agent = 9
   AND a.last_call > DATE(NOW()) - INTERVAL 12 MONTH
 GROUP BY a.calling_agent, DATE_FORMAT(a.last_call,'%d, %W')
 ORDER BY a.calling_agent, DATE_FORMAT(a.last_call,'%d, %W') DESC

打包查询

我想我可能会对此查询的设计提供一些见解,以及它的目的是什么。我保留了原始外部查询中的FROMWHERE子句。我刚给了calls表的别名,并将谓词重写为我认为更简单的表格,并且我更习惯使用。

对于GROUP BY,我添加了calling_agent,因为我们希望将所有代理放在一起似乎没有意义。 (这取决于你是否与规范相匹配。)我这样做是因为在WHERE子句中没有引用calling_agent。 (reg_calling_agent上有一个等同谓词,但这是一个不同的列。)

我替换了EXTRACT(DAY FROM )表达式,因为它只返回1到31之间的整数值。并且它似乎没有意义将所有&#34;第4天&#34;所有月份。我选择使用SELECT列表中的表达式;因为这是规范模式...返回SELECT列表中GROUP BY子句中使用的表达式,因此客户端将能够区分结果中的哪一行属于哪个组标识符。

我还使用表别名限定所有列引用,以帮助未来的读者。我们熟悉在复杂查询中遵循该模式。很自然地,我们将相同的模式扩展到更简单的查询,即使它不是必需的。

最大的变化是派生的 breaks 列。 (我改编自&#39;刹车&#39;,因为看起来这个查询正在做的是找出当calling_agents没有拨打/接听电话时,工人是&#34;服用休息&#34;。(这完全是我的猜测。)

有一个SEC_TO_TIME功能,所有这一切都是重新格式化结果。

有一个SUM()聚合。这只是对{&#34;&#34;组&#34;中a中每一行的值进行总计。

真正的&#34;肉类&#34;是相关子查询。对于外部查询返回的每一行(即满足外部查询的calls子句的WHERE中的每一行),这样做... ...我们将运行另一行{{1 }}。它会去寻找下一个&#34;下一个&#34;由同一SELECT拨打/接听的电话。为此,{&#34; next&#34;上的calling_agent call需要匹配来自外部查询的行中的值...

calling_agent

此外,后续&#34;呼叫的日期时间/时间戳&#34;需要在之后在外部查询的行的日期时间/时间戳之后<...>

            WHERE c.calling_agent = a.calling_agent

而且,我们只想查找与上次通话相同的日历日期(年,月,日)的通话。 (这使我们无法将四天后的电话视为&#34;随后的电话。)

              AND c.last_call     > a.last_call

而且,在所有可能的后续调用中,我们只需要第一个,所以我们按日期时间/时间戳排序,然后只选择第一个。

              AND c.last_call     < DATE(a.last_call)+INTERVAL 1 DAY

如果我们没有获得一行,子查询将返回NULL。如果我们确实获得了一行,那么我们要做的下一件事就是检查此次调用的日期时间/时间戳是否超过上一次调用后的10分钟。我们使用原始查询中的相同 ORDER BY c.last_call LIMIT 1 表达式来推导调用之间的秒数,并将其与10分钟进行比较。如果差距大于10分钟,我们将其视为&#34; break&#34;,我们将差异作为秒数返回。否则,我们只返回一个NULL,好像我们没有找到&#34; next&#34;行。

TIMESTAMPDIFF

这是ANSI特定的ANSI标准形式的简写:

                  IF(TIMESTAMPDIFF(SECOND, a.last_call, c.last_call) > 600
                    ,TIMESTAMPDIFF(SECOND, a.last_call, c.last_call)
                    ,NULL
                  ) AS `gap`

(注意: CASE WHEN TIMESTAMPDIFF(SECOND, a.last_call, c.last_call) > 600 THEN TIMESTAMPDIFF(SECOND, a.last_call, c.last_call) ELSE NULL END AS `gap` 可以省略,这在功能上是等效的,因为省略ELSE NULLNULL是默认值。我在此处包含它是为了完整性,并用于比较MySQL ELSE函数。)

最后,我们在IF()列表的GROUP BY子句中包含所有表达式。 (这不是必需的,但它是通常的模式。如果省略这些表达式,应该有一个非常明显的原因,为什么它们被省略。例如,如果外部查询在{上有一个等式谓词{1}},例如

SELECT

然后我们知道查询返回的任何行都会为calling_agent返回 AND a.calling_agent = 86 的值,因此我们可以省略86列表中的表达式。但是,如果我们省略了一个等式谓词,或者更改它以便可以返回多个calling_agent,那就像:

SELECT

然后在SELECT列表中没有calling_agent,我们无法分辨哪些行是哪个calling_agent。如果我们打算对表达式执行 AND (a.calling_agent = 86 OR a.calling_agent = 99) ,我们通常希望将表达式包含在calling_agent列表中;这是正常模式。