MySQL聚合查询的行为不符合预期

时间:2014-08-15 11:02:12

标签: mysql sql

我正在尝试聚合一些通话记录并做错事 - 但不确定是什么。

我有以下查询:

SELECT 
    c.name, 
    IF(MONTH(start) = 1, SUM(duration),0) AS 'Jan',
    IF(MONTH(start) = 2, SUM(duration),0) AS 'Feb',
    IF(MONTH(start) = 3, SUM(duration),0) AS 'Mar',
    IF(MONTH(start) = 4, SUM(duration),0) AS 'Apr',
    IF(MONTH(start) = 5, SUM(duration),0) AS 'Jun',
    IF(MONTH(start) = 6, SUM(duration),0) AS 'Jul',
    IF(MONTH(start) = 7, SUM(duration),0) AS 'Aug'
FROM
    call_history ch, client c
WHERE
    ch.client_reseller_id = c.id
GROUP BY ch.client_reseller_id
ORDER BY c.name;

字段start包含通话的日期/时间。该表仅包含一年的记录,因此无需担心未按年过滤。

我得到的结果并不像预期的那样:

+--------------------------------+----------+------+------+--------+------+------+------+
| name                           | Jan      | Feb  | Mar  | Apr    | Jun  | Jul  | Aug  |      
|+-------------------------------+----------+------+------+--------+------+------+------+
| Come company                   |  5243080 |    0 |    0 |      0 |    0 |    0 |    0 |
| Other cust                     |  4085085 |    0 |    0 |      0 |    0 |    0 |    0 |
| Someone                        |  1449543 |    0 |    0 |      0 |    0 |    0 |    0 |
| Demo Reseller                  |     2342 |    0 |    0 |      0 |    0 |    0 |    0 |
+--------------------------------+----------+------+------+--------+------+------+------+

我做错了什么?

2 个答案:

答案 0 :(得分:3)

像这样改变

SELECT 
    c.name, 
   SUM(IF(MONTH(start) = 1, duration,0)) AS 'Jan',
    SUM(IF(MONTH(start) = 2, duration,0)) AS 'Feb',
    SUM(IF(MONTH(start) = 3, duration,0)) AS 'Mar',
    SUM(IF(MONTH(start) = 4, duration,0)) AS 'Apr',
    SUM(IF(MONTH(start) = 5, duration,0)) AS 'Jun',
    SUM(IF(MONTH(start) = 6, duration,0)) AS 'Jul',
    SUM(IF(MONTH(start) = 7, duration,0)) AS 'Aug'
FROM
    call_history ch, client c
WHERE
    ch.client_reseller_id = c.id
GROUP BY ch.client_reseller_id
ORDER BY c.name;

答案 1 :(得分:1)

你想:

  SELECT c.name, 
         SUM((MONTH(ch.start)=1)*ch.duration) 'Jan',
         SUM((MONTH(ch.start)=2)*ch.duration) 'Feb',
         SUM((MONTH(ch.start)=3)*ch.duration) 'Mar',
         SUM((MONTH(ch.start)=4)*ch.duration) 'Apr',
         SUM((MONTH(ch.start)=5)*ch.duration) 'Jun',
         SUM((MONTH(ch.start)=6)*ch.duration) 'Jul',
         SUM((MONTH(ch.start)=7)*ch.duration) 'Aug'
    FROM call_history ch
    JOIN client c
      ON c.id = ch.client_reseller_id
GROUP BY c.name
ORDER BY c.name;

这使用了在MySQL中将布尔值评估为true => 1false => 0的事实。

它避免使用IF这是一个更合乎逻辑的分支..但我不确定哪个会更快,因为*操作,基准测试可能很有趣。 IF也仅限于MySQL,您可以使用标准CASE

我可以承认有些人可能会发现IF/CASE比使用BOOLEAN =>更具可读性10动态,再次可能仅限于MySQL。

我还在列上添加了表别名,将GROUP BY更改为更明显的c.name并使用了明确的JOIN

这些是可读性问题..

包含表别名意味着您可以单独读取查询并知道每列的来源...例如在发布问题时!我不得不假设startduration来自call_history,这在这里无所谓,但在其他问题上绝对可以。

我倾向于GROUP BY MySQL SELECT中的非聚合列,以确保我没有遗漏任何内容。如果我错了,请纠正我,但我认为其他引擎需要它。如果您有c.name个重复内容,我会同时选择c.idc.name以及GROUP BY,以消除任何混淆。

至于使用隐式JOIN,您可以看到讨论here