每个GROUP BY的COUNT都有不存在的值

时间:2013-02-11 16:29:06

标签: mysql sql pdo mysqli

我有下表

id  rid     eId     vs  isN
24  3   22  2   1
25  3   21  2   1
26  60  21  2   1
27  60  21  2   1
28  60  21  2   1
29  60  21  2   1
30  60  21  2   1
31  60  21  2   1
32  81  21  2   1
35  60  22  2   1
36  81  22  2   1
37  0   22  2   1
38  60  22  2   1
39  81  22  2   1
40  0   22  2   1
41  60  22  2   1
42  81  22  2   1
43  3   22  2   1

eId我有8个不同的号码 我想要计算这八个不同的eid,甚至算作“0”我想得到的是一个数组包含8个值,键应该是八个不同的名称。每次算起来,“vs”是3个不同的数字 我想要“rid”=%d和“vs”=%d(特定摆脱和特定vs)

SELECT count(*) as count FROM notification 
WHERE rid = 60 AND vs = 2 AND isN = 1 GROUP BY eId 

rid=>60,21=>6,22=>3,vs=>2,isN=>1  

(这是我上面提到的)

rid=>60,21=>6,22=>3,23=>0,33=>0,34=>0,35=>0,36=>0,41=>0,42=>0,vs=>2,isN=>1

(这就是我想要的。八个计算,当然这个数字计算不存在于eId所以我想返回为零)

3 个答案:

答案 0 :(得分:1)

您需要的是一个包含您想要输出的所有值的驱动程序表。然后,您可以将外部连接到实际数据:

SELECT count(notification.eid) as count
FROM (select distinct eid
      from notification
     ) drivers left outer join
     (select *
      from notification
      WHERE rid = %d AND vs = %d AND isN = 1
     ) n
     on driver.eid = notification.eid
GROUP BY driver.eId

您还应该在eid子句中包含select,除非您依赖于输出的最终排序(MySQL与其他任何数据库不同,确保在{{}之后对结果进行排序{1}}。)

答案 1 :(得分:1)

这是获取指定结果集的一种方法:

 SELECT d.rid           AS `rid`
      , SUM(n.eid<=>21) AS `21`
      , SUM(n.eid<=>22) AS `22`
      , SUM(n.eid<=>23) AS `23`
      , SUM(n.eid<=>33) AS `33`
      , SUM(n.eid<=>34) AS `34`
      , SUM(n.eid<=>35) AS `35`
      , SUM(n.eid<=>36) AS `36`
      , SUM(n.eid<=>41) AS `41`
      , SUM(n.eid<=>42) AS `42`
      , d.vs            AS `vs`
      , d.isN           AS `isN`
   FROM ( SELECT %d AS rid, %d AS vs, 1 AS isN ) d
   LEFT
   JOIN notification n
     ON n.rid = d.rid
    AND n.vs  = d.vs
    AND n.isN = d.isN
  GROUP
     BY d.rid
      , d.vs
      , d.isN

注意:表达式(n.eid<=>21)IF(n.eid=21,1,0)的简写,或者是ANSI标准CASE WHEN n.eid = 21 THEN 1 ELSE 0 END。这给出了0或1,然后可以用SUM函数聚合。

您可以使用以下任何形式获得相同的结果:

     , SUM(n.eid<=>21)                             AS `21`
     , COUNT(IF(n.eid=22,1,NULL))                  AS `22`
     , SUM(IF(n.eid=23,1,0))                       AS `23`
     , COUNT(CASE WHEN n.eid = 33 THEN 1 END)      AS `33`
     , SUM(CASE WHEN n.eid = 34 THEN 1 ELSE 0 END) AS `34`

我们在这里使用的“技巧”是我们保证内联视图别名为d将返回一行。然后我们使用LEFT JOIN运算符从notification表中选取所有“匹配”行。 GROUP BY将强制将所有这些行折叠(聚合)回到单行。我们在每一行上使用条件测试来查看它是否包含在给定的计数中,“技巧”是为每一行返回0或1,然后将所有0和1s来计算。

注意:如果您使用COUNT(expr)聚合,则希望expr在要包含在计数中时返回非NULL,并且当行不包含时返回NULL被列入计票。

如果您使用SUM(expr),那么当行要包含在计数中时,您希望expr返回1,否则返回0。 (我们想要一个0而不是NULL,以便在没有要包含的行时保证SUM(expr)将返回“零计数”(即0而不是NULL)。(当然,我们可以使用IFNULL函数将NULL替换为0,但在这种情况下,它足够简单以避免需要。)


请注意,这种“计数”方法的一个优点是它可以轻松扩展以获得“组合”计数,或者包含几个不同计数的行。 e.g。

    , SUM(IF(n.eid IN (41,42),1,0)) AS `total_41_and_42`

会给我们一个总计数eid = 41和eid = 42行。 (这不是一个很好的例子,因为我们可以通过将两个计数加在一起来在客户端轻松计算出来。但是如果你做了更精细的计数,并且想要计算多个单行,那真的会成为一个优势列...

   , SUM(IF(n.eid=42,1,0))            AS eid_42
   , SUM(IF(n.eid=42 AND foo=1,1,0)   AS eid_42_foo_1
   , SUM(IF(n.eid=42 AND foo=2,1,0))  AS eid_42_foo_2

我们可以通过notification表中的“一次通过”获得所有这些单独的计数。如果我们尝试在WHERE子句中执行这些检查,我们可能需要多次遍历表。

答案 2 :(得分:0)

所以,基本上你要找的是这个?...

 SELECT rid,eid,vs, COUNT(*) FROM notification GROUP BY rid,eid,vs;
 +-----+-----+----+----------+
 | rid | eid | vs | COUNT(*) |
 +-----+-----+----+----------+
 |   0 |  22 |  2 |        2 |
 |   3 |  21 |  2 |        1 |
 |   3 |  22 |  2 |        2 |
 |  60 |  21 |  2 |        6 |
 |  60 |  22 |  2 |        3 |
 |  81 |  21 |  2 |        1 |
 |  81 |  22 |  2 |        3 |
 +-----+-----+----+----------+
 7 rows in set (0.11 sec)