如何在查询中包含零计数结果

Posted

技术标签:

【中文标题】如何在查询中包含零计数结果【英文标题】:How to include zero-count results in query 【发布时间】:2017-12-11 19:42:23 【问题描述】:

我有这两张表:

运营商:

Id Nome
--+----
 1 JDOE
 2 RROE
 3 MMOE

来电:

Id CallDate OpId
--+--------+----
 1 20161228    2
 2 20161228    3
 3 20161228    2
 4 20161228    3
 5 20170104    1
 6 20170104    2
 7 20170104    1    

这个查询:

SELECT Operators.id, Operators.Nome, Count(Calls.OpId) AS CountCalls
FROM Operators LEFT JOIN Calls ON Operators.id = Calls.OpId
GROUP BY Calls.CallDate, Operators.id, Operators.Nome
HAVING Calls.CallDate=20170104;

返回:

Id Nome CountCalls
--+----+----------
 1 JDOE          2
 2 RROE          1

我怎样才能让它返回这个呢?

Id Nome CountCalls
--+----+----------
 1 JDOE          2
 2 RROE          1
 3 MMOE          0

也就是说,如何在任何查询中也包括在左连接表中没有出现的主表的零结果,至少在查询过滤条件定义的数据切片中?

这是 Access 2013。

我已经阅读了this answer,但看不出它与我正在做的事情有何不同。

【问题讨论】:

不要在 WHERE 子句谓词中引用任何 LEFT JOINed 表列,不允许 NULL 值。 【参考方案1】:

因为您在 HAVING 子句中引用了 Calls.CallDate,所以您正在删除没有调用的运算符。如果没有调用,那么CallDate 将是NULL,而NULL=20170104 不正确,因此这些行被排除在外。您需要将此谓词移动到您的连接子句:

SELECT Operators.id, Operators.Nome, Count(Calls.OpId) AS CountCalls
FROM Operators LEFT JOIN Calls ON (Operators.id = Calls.OpId AND Calls.CallDate=20170104)
GROUP BY Calls.CallDate, Operators.id, Operators.Nome;

你也不需要按Calls.CallDate分组,反正你只有一个,所以你可以用:

SELECT Operators.id, Operators.Nome, Count(Calls.OpId) AS CountCalls
FROM Operators LEFT JOIN Calls ON (Operators.id = Calls.OpId AND Calls.CallDate=20170104)
GROUP BY Operators.id, Operators.Nome;

顺便说一句,HAVING 是错误的运算符。 HAVING 用于过滤聚合,因为你没有过滤聚合,你应该简单地使用WHERE

SELECT Operators.id, Operators.Nome, Count(Calls.OpId) AS CountCalls
FROM Operators LEFT JOIN Calls ON Operators.id = Calls.OpId
WHERE Calls.CallDate=20170104
GROUP BY Calls.CallDate, Operators.id, Operators.Nome;

如果您想过滤CountCalls,您可以使用HAVING,例如,如果您只需要拨打超过1次电话的操作员,您可以使用:

SELECT Operators.id, Operators.Nome, Count(Calls.OpId) AS CountCalls
FROM Operators LEFT JOIN Calls ON Operators.id = Calls.OpId
WHERE Calls.CallDate=20170104
GROUP BY Calls.CallDate, Operators.id, Operators.Nome
HAVING Count(Calls.OpId) > 1;

这只会返回

Id Nome CountCalls
--+----+----------
 1 JDOE          2

【讨论】:

只是一个观察:由于这些更改已完成,MS-Access 将拒绝在设计模式下显示此查询,它将仅作为 SQL 或结果视图提供。显然,它的接口无法处理这个更复杂的 Join 子句。对 MS 感到羞耻... 嗯,对于任何非典型连接都是如此,我不会因此而羞辱 MS。他们只是无法在设计视图中正确地表示它,并且不应该在查询中存在未在此处表示的隐藏元素。【参考方案2】:

试试下面的查询:

SELECT o.id, o.name, count(c.opid) as countcalls
FROM Operators o LEFT JOIN calls c ON o.id = c.opid AND c.calldate=20170104
GROUP BY c.calldate, o.id, o.name
ORDER BY o.id;

输出:

+------+------+------------+
| id   | name | countcalls |
+------+------+------------+
|    1 | JDOE |          2 |
|    2 | RROE |          1 |
|    3 | MMOE |          0 |
+------+------+------------+

【讨论】:

以上是关于如何在查询中包含零计数结果的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL 查询结果采用科学计数法,如何改变属性。

如何使用Apache Jena获得“选择计数(*)”查询的结果?

如何对查询结果进行“计数”并将其作为 JSON 格式的数组发送?

如何在同一张表上计算不同结果的计数[重复]

如何在 unpivot 查询中包含 id 列

如何在 Gorm 中留下连接子查询计数