MySQL:选择前10名并聚合其他人

时间:2015-03-23 04:26:18

标签: mysql subquery aggregate-functions timeserieschart

我有一个监控应用程序,它在MySQL DB中存储指标,并使用RRDtool绘制时间序列图。 DB中的表有3列:timestamp,RRDKeyID和value,即。那里有一个包含以下数据的表:

ts1 | user2 | some_value
ts1 | user4 | some_value
ts1 | user5 | some_value
ts1 | user7 | some_value
ts2 | user1 | some_value
ts2 | user2 | some_value
ts2 | user3 | some_value
ts2 | user5 | some_value
ts2 | user8 | some_value
ts3 | user3 | some_value
ts3 | user4 | some_value
ts3 | user5 | some_value
ts3 | user7 | some_value
...

数据以1分钟的间隔收集,用户(如您所知)可以自由连接到系统并生成指标,因此在任何给定时刻,系统中的许多用户都低于总数。用户总数约为1k,并且总有几百个用户在线,这意味着在度量表中有几百行具有相同的时间戳。当我生成一个图形时,我不想为每个用户显示该系列,因为由于画布尺寸太小,有限的颜色调色板,图例太长等等,图形是不可读的。这就是为什么我改为仅生成图形的原因按度量标准值排名前10位的用户将单独显示,而所有其他用户则汇总到一个黑色区域。我就是这样做的:

  1. 我从表中获得前10名:

    选择RRDKeyID,avg(value)作为平均值 来自metric_table 其中ts介于' t1'和&#t;' 按RRDKeyID分组 由avg desc订购 限制10

  2. 然后在Perl中生成RRDtool命令,绘制图形,其中每个用户(RRDKeyID)都有自己的DEF,但只有SQL查询结果中的用户的DEF直接绘制,其余的都汇总在一起:

    CDEF:他人= 0,user11,ADDNAN,USER12,ADDNAN,user13,ADDNAN,...,用户N

  3. 然后" AREA:其他#000000:其他:堆叠"显示在图表上。

    现在我正在研究应用程序的下一个版本,由于数据库架构的变化,RRDtool将被D3.js取代,因此我需要另一种方法来生成我的top10 +其他数据。我不想将原始数据发送到客户端并进行处理,因为数据集可能非常大,处理可能需要很长时间和CPU,所以我宁愿在服务器端。我怀疑有可能以下列格式得到我想要的结果:

     ts   | user   | value 
    ------+--------+-------
     t1   | u1     | v 
     t1+1 | u1     | v 
     t1+2 | u1     | v 
    ...
     t2   | u1     | v 
     t1   | u2     | v 
    ...
     t2   | u2     | v 
     t1   | u3     | v 
    ...
     t2   | u3     | v 
    ...
    ...
     t1   | u10    | v 
    ...
     t2   | u10    | v 
     t1   | others | v 
    ...
     t2   | others | v 
    

    一次性(顺序不重要)使用嵌套查询,其中有一个子查询在给定的时间范围内选择用户名(RRDKeyIDs)和avg(值),然后top查询打印用户名的结果如果在子查询结果中找到它们或以其他方式将它们添加到其他人。我不确切地知道如何在SQL中表达这个想法,所以如果有人能提出解决方案,我将不胜感激。

2 个答案:

答案 0 :(得分:1)

如何枚举MySQL中的值然后重新聚合?

select (case when rn <= 10 then RRDKeyId end) as RRDKeyId,
       sum(sumv) / sum(cnt) as avgvalue
from (select (@rn := @rn + 1) as rn, mt.*
      from (select RRDKeyID, sum(value) as sumv, count(*) as cnt
            from metric_table
            where ts between 't1' and 't2'
            group by RRDKeyID 
            order by avg(value) desc
           ) mt cross join
           (select @rn := 0) vars
      ) mt
group by (case when rn <= 10 then RRDKeyId end);

这使用NULL作为额外组。如果你愿意,你可以加上另一个名字。

答案 1 :(得分:0)

据我了解你,你想要

  1. 发现前10名
  2. 获取数据以绘制它们
  3. 取得其余的平均值,制作第11个情节。
  4. 类似的东西:

    $list = 
    SELECT  GROUP_CONCAT(RRDKeyID)
        FROM  
          ( SELECT  RRDKeyID, AVG(value)
                FROM  metric_table
                WHERE  ts between 't1' AND 't2'
                group by  RRDKeyID
                order by  AVG(value) desc
                limit  10 
          ) x; 
    SELECT  ...              WHERE  RRDKeyID     IN ( $list ) ...; -- top 10
    SELECT  ... AVG(...) ... WHERE  RRDKeyID NOT IN ( $list ) ...; -- the rest
    

    将这三个语句组合成单个查询会很困难。 IN将变为JOIN / LEFT JOIN,并且需要重复原始查询。