在单个查询中选择行总数,field1 = 1的总数,field1 = 0的总数?

时间:2015-05-20 14:34:55

标签: mysql count subtotal

我有下表:

id  | command_id    | started_at            | ended_at              | rows_involved | completed
-----------------------------------------------------------------------------------------------
1   | 1             | 2015-05-20 12:02:25   | 2015-05-20 12:02:28   | 1             | 1
2   | 1             | 2015-05-20 12:02:47   | NULL                  | NULL          | 0
3   | 1             | 2015-05-20 12:11:10   | NULL                  | NULL          | 0
4   | 1             | 2015-05-20 12:11:46   | NULL                  | NULL          | 0
5   | 1             | 2015-05-20 12:12:25   | NULL                  | NULL          | 0

我想获取started_at is '2015-05-20' AND commande_id = 1的COUNT行,我希望获得2个子总计,1是这些行的总数,其中completed = 1和1是这些行的总和completed = 0 1}}。

预期的数据集如下:

array(4) {
    ["totalRows"]=> 5
    ["name"]=> "evo:send_post_registration_mail_1"
    ["totalCompleted"] => 1
    ["totalUncompleted"] => 4
}

"名称" column不重要,是与command_id字段上的另一个表的连接。

我当前的查询如下,但它不会获取2个小计:

SELECT COUNT(s0_.id) AS totalRows, s1_.name AS name 
FROM sf_command_executions s0_ 
INNER JOIN sf_commands s1_ ON (s1_.id = s0_.command_id) 
WHERE DATE_FORMAT(s0_.started_at,'%Y-%m-%d') = '2015-05-20' 
GROUP BY s0_.command_id 

我可以在该单个查询中获取这两个小计吗?

3 个答案:

答案 0 :(得分:1)

您可以使用条件聚合。在SELECT列表中使用这样的表达式...

SELECT ...
     , SUM(IF(s0_.completed=1,1,0)) AS tot_completed_1
     , SUM(IF(s0_.completed=0,1,0)) AS tot_completed_0

您可以使用(更符合ANSI标准的)CASE表达式来实现相同的目的:

     , SUM(CASE WHEN s0_.completed = 1 THEN 1 ELSE 0 END) AS tot_completed_1

或者你可以使用更短的MySQL速记,因为布尔表达式返回值为1,0或NULL:

     , SUM(s0_.completed=1) AS tot_completed_1

修改

以下内容并未解决您提出的问题(请参阅上文,了解您提出的问题的答案)。但是我想指出started_at列上的谓词(即WHERE子句)。

 WHERE DATE_FORMAT(s0_.started_at,'%Y-%m-%d') = '2015-05-20'
       ^^^^^^^^^^^^              ^^^^^^^^^^^^

围绕列引用的DATE_FORMAT函数阻止MySQL使用索引范围扫描操作来满足该谓词。

也就是说,MySQL必须在表中的每个行上评估该函数,然后将表达式的结果与文字值进行比较。

如果将started_at定义为DATETIMETIMESTAMP,我们可以将其重写为等效条件,但在 started_at上柱。这将允许MySQL使用索引范围扫描操作。例如,我们可以像这样编写相同的行:

 WHERE s0_.started_at >= '2015-05-20'
   AND s0_.started_at <  '2015-05-20' + INTERVAL 1 DAY

如果将started_at定义为DATE,我们可以使用相等比较来引用裸列。不需要DATE_FORMAT功能。

如果我们必须使用函数进行某种转换以便可以比较值,我们更喜欢用函数来包裹文字而不是列引用。在文字周围,该功能只需要评估一次。

在这种情况下,实际上并不需要这样做,但仅作为将函数包装在函数中的示例:

 WHERE s0_.started_at >= STR_TO_DATE('2015-05-20','%Y-%m-%d')
   AND s0_.started_at <  STR_TO_DATE('2015-05-20','%Y-%m-%d') + INTERVAL 1 DAY

请注意(再次)使用STR_TO_DATE功能并非实际需要;这只是展示一种模式。如果我们确实需要进行转换,我们更倾向于在字面上而不是在列上,以允许MySQL使用started_at上的可用索引。

答案 1 :(得分:1)

您可以使用条件和作为

SELECT 
COUNT(s0_.id) AS totalRows, 
s1_.name AS name ,
sum(s0_.completed=1) as totalCompleted,
sum(s0_.completed=0) as totalUncompleted
FROM sf_command_executions s0_ 
INNER JOIN sf_commands s1_ ON (s1_.id = s0_.command_id) 
WHERE DATE_FORMAT(s0_.started_at,'%Y-%m-%d') = '2015-05-20' 
GROUP BY s0_.command_id 

答案 2 :(得分:0)

试试这个:

SELECT COUNT(s0_.id) AS totalRows, s1_.name AS name, 
(select count(S2_.id) from sf_command_executions S2_ where s0_.command_id=S2_.command_id and s2_.completed = 1) AS totalCompleted,
(select count(S2_.id) from sf_command_executions S2_ where s0_.command_id=S2_.command_id and s2_.completed = 0) AS totalUncompleted
FROM sf_command_executions s0_ 
INNER JOIN sf_commands s1_ ON (s1_.id = s0_.command_id) 
WHERE DATE_FORMAT(s0_.started_at,'%Y-%m-%d') = '2015-05-20' 
GROUP BY s0_.command_id