"其他结果"饼图数据

时间:2015-05-04 06:47:54

标签: mysql

例如,我们假设我有一张表格,其中列出了人名,以及他们来自的国家/地区的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} )

1 个答案:

答案 0 :(得分:2)

我会使用您的第一个查询,并在应用程序代码中决定自己显示多少个国家/地区以及显示为“其他”组的数量。例如,如果结果集中的前三个国家/地区包含95%的用户群,则该图表将包含17个国家/地区,这些国家/地区的饼图将无法区分,因为它们的百分比低于1%。

您可以在显示独立或按百分比显示的国家/地区数量上设置阈值,但不管怎样,从数据库中获取的实际值可能会使图表在某些情况下看起来很糟糕。

声明

此答案是作为概念验证提供的。我不建议在生产代码中使用它有两个原因:

  • 由于内部查询,它很慢;在闲置服务器上处理376万行的表需要1.06秒;根据您的环境和应用,这个时间可能是可接受的或巨大的;
  • 它不够灵活;见上面的解释。

代码

让我们分两步构建查询。首先,让我们编写一个查询,以绝对值和百分比形式显示图表中显示的数字:

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相乘。

第2步

我们现在可以处理上述查询生成的结果集,汇总列表底部的国家/地区:

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

isGroup10)用于获取列表末尾的组,位于独立国家/地区(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非常小。