例如,我们假设我有一张表格,其中列出了人名,以及他们来自的国家/地区的ID。如果我要制作一个饼图,显示有多少人来自每个国家,并且我展示了所有这些,那将在饼图上的这么多部分上划线,这几行几乎无法区分。
所以,让我们说查询看起来像这样:
SELECT COUNT(name) AS n, countryId
FROM persons
GROUP BY countryId
ORDER BY n DESC
如果我通过在其末尾添加LIMIT 20
将此查询中的结果数限制为前20,这将使整个饼图更具可读性,因为它只显示20个分区。但它的视觉表现是不正确的。整个饼图将不再代表所有人,它只代表前20个国家。
所以,从那里开始,如果我想让饼图的整个圆圈实际上代表我在数据库中的所有人,那么我想要的是那个饼图的第21部分,它们就等于,让我们说14%,并且将包含属于任何不在前20名的国家的所有人。
我的问题是,这可以在一个查询中完成吗?如果是这样,怎么样?
我知道我可以在这里做另一个查询,它会计算排在前20位的国家/地区的结果数量,但如果可能的话,我会尽量避免对此解决方案进行两次查询。
SELECT COUNT(name) AS n
FROM persons
WHERE countryId NOT IN ( {$concatenatedIdsFromPreviousQuery} )
答案 0 :(得分:2)
我会使用您的第一个查询,并在应用程序代码中决定自己显示多少个国家/地区以及显示为“其他”组的数量。例如,如果结果集中的前三个国家/地区包含95%的用户群,则该图表将包含17个国家/地区,这些国家/地区的饼图将无法区分,因为它们的百分比低于1%。
您可以在显示独立或按百分比显示的国家/地区数量上设置阈值,但不管怎样,从数据库中获取的实际值可能会使图表在某些情况下看起来很糟糕。
此答案是作为概念验证提供的。我不建议在生产代码中使用它有两个原因:
让我们分两步构建查询。首先,让我们编写一个查询,以绝对值和百分比形式显示图表中显示的数字:
SELECT countryId, COUNT(*) AS cnt,
COUNT(*)*100/(SELECT COUNT(*) FROM persons) AS percent
FROM persons
GROUP BY countryId
ORDER BY cnt DESC
此查询计算每个国家/地区的人数,并计算表格中人员总数的比率,以百分比表示。
一些评论:
内部查询SELECT COUNT(*) FROM persons
计算表中的人数。需要计算百分比。它可以在单独的查询中提取,其值存储在变量中:
SELECT COUNT(*) INTO @total FROM persons;
SELECT countryId, COUNT(*) AS cnt,
COUNT(*)*100/@total AS percent
FROM persons
GROUP BY countryId
ORDER BY cnt DESC
但它不会改善执行时间。 MySQL非常聪明,只能运行内部查询一次并将结果用于外部查询(它在内部执行变量技巧,因为内部查询不依赖于外部查询,它总是返回相同的值)。
因为它乘以100
,所以percent
中计算的值是要在图表中显示的最终值。对于通常的处理,最好将比率计算为COUNT(*)/@total
,并且仅当值显示在UI中时,才将值与100
相乘。
我们现在可以处理上述查询生成的结果集,汇总列表底部的国家/地区:
SELECT IF(percent < 5, -1, countryId) AS groupId,
IF(percent < 5, 1, 0) AS isGroup,
SUM(cnt) AS cnt, SUM(percent) AS percent
FROM (
SELECT countryId, COUNT(*) AS cnt,
COUNT(*)*100/(SELECT COUNT(*) FROM persons) AS percent
FROM persons
GROUP BY countryId
) t
GROUP BY groupId
ORDER BY isGroup ASC, percent DESC
查询使用阈值5%
来确定国家/地区是单独显示还是添加到“其他国家/地区”组。
groupId
为-1
,对于图表中显示的国家/地区为countryId
。
isGroup
(1
或0
)用于获取列表末尾的组,位于独立国家/地区(ORDER BY isGroup ASC
)之后。然后,按percent DESC
对独立国家/地区列表进行排序。 isGroup
列表中不需要SELECT
,您可以将其定义直接替换为ORDER BY
子句。
如果您在源代码中有国家/地区列表,那么这是您的查询。但是如果你在数据库的表中有列表,那么你可能也需要得到它们的名字。加入countries
表后,查询变为:
SELECT IF(percent < 5, '-- other countries --', c.countryName) AS groupId,
SUM(cnt) AS cnt, SUM(t.percent) AS percent
FROM (
SELECT p.countryId, COUNT(*) AS cnt,
COUNT(*)*100/(SELECT COUNT(*) FROM persons) AS percent
FROM persons p
GROUP BY countryId
) t
LEFT JOIN countries c ON t.countryId = c.countryId
GROUP BY groupId
ORDER BY IF(t.percent < 5, 1, 0) ASC, percent DESC
加入countries
表不会显着增加执行时间。世界上大约有200个国家,表countries
非常小。