当我两次加入时,为什么COUNT的结果会加倍?

时间:2017-07-13 12:02:12

标签: sql postgresql postgresql-9.6

我有这张桌子

设备

 id      name         groupId     serviceId
791   Mamie Ortega      205         1832

 id   serviceId
205     1832

记录

 id          date                      deviceId
792   2017-07-13 13:30:19.740360         784
793   2017-07-13 13:30:19.742799         784

警报

 id    status    deviceId
241      new        784
242      new        784 

我正在运行此查询

SELECT device.id, device.name, COUNT(records.id) AS "last24HMessagesCount", COUNT(alarms.id) AS "activeAlarmsCount"
FROM device
  INNER JOIN "group" AS "group" ON "device"."groupId" = "group"."id" AND "group"."id" = '205'
  LEFT OUTER JOIN "record" AS "records" ON "device"."id" = "records"."deviceId" AND "records"."date" > '2017-07-12 11:43:02.838 +00:00'
  LEFT OUTER JOIN "alarm" AS "alarms" ON "device"."id" = "alarms"."deviceId" AND "alarms"."status" = 'new'
WHERE "device"."serviceId" = 1832
GROUP BY device.id;

哪个给我这个结果

 id      name       last24HMessagesCount      activeAlarmsCount   
791   Mamie Ortega         4                          4

这个结果是错误的,我应该为last24HMessagesCount和activeAlarmsCount提供2个。

如果我删除其中一个计数,例如last24HMessagesCount并执行

SELECT device.id, device.name, COUNT(alarms.id) AS "activeAlarmsCount"
FROM device
  INNER JOIN "group" AS "group" ON "device"."groupId" = "group"."id" AND "group"."id" = '205'
  LEFT OUTER JOIN "alarm" AS "alarms" ON "device"."id" = "alarms"."deviceId" AND "alarms"."status" = 'new'
WHERE "device"."serviceId" = 1832
GROUP BY device.id;

结果是正确的

 id      name       activeAlarmsCount   
791   Mamie Ortega         2

我不明白,为什么计数加倍?

2 个答案:

答案 0 :(得分:4)

回答非常简单。您有两个record和两个alarm。你加入这些并得到四条记录,你算了。

您可以通过计算不同的ID来解决此问题:

COUNT(DISTINCT records.id) AS "last24HMessagesCount",
COUNT(DISTINCT alarms.id) AS "activeAlarmsCount"

但我不建议这样做。你为什么要加入recordalarm呢?它们没有直接关系。您想要加入的内容是record的数量和alarm的数量。所以在加入之前聚合:

SELECT 
  device.id, 
  device.name, 
  records.cnt AS "last24HMessagesCount", 
  alarms.cnt AS "activeAlarmsCount"
FROM device
LEFT OUTER JOIN 
(
  SELECT deviceId, count(*) AS cnt
  FROM record
  WHERE "date" > '2017-07-12 11:43:02.838 +00:00'
  GROUP BY deviceId
) AS records ON device.id = records.deviceId
LEFT OUTER JOIN 
(
  SELECT deviceId, count(*) AS cnt
  FROM alarm
  WHERE status = 'new'
  GROUP BY deviceId
) AS alarms ON device.id = alarms.deviceId
WHERE device.serviceId = 1832
  AND device.groupId = 205;

(我已将不必要的联接删除到“组”表。)

答案 1 :(得分:3)

您的连接正沿着两个维度生成笛卡尔积。最简单的解决方案是使用COUNT(DISTINCT)

SELECT device.id, device.name,
       COUNT(DISTINCT records.id) AS "last24HMessagesCount",
       COUNT(DISTINCT alarms.id) AS "activeAlarmsCount"

如果计数不是很大,这是有效的。替代解决方案更具可扩展性。那就是在 LEFT JOIN之前进行聚合或使用相关子查询(或横向连接)。