2015-03-23 2 views
0

У меня есть приложение для мониторинга, которое хранит показатели в базе данных MySQL и использует RRDtool для рисования графиков временных рядов. Таблицы в БД имеют 3 столбца: временная метка, RRDKeyID и значение, т.е. есть таблица с данными, такие как:MySQL: выберите топ-10 и запишите другие

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-минутными интервалами и пользователей (как вы их знаете) свободно подключаться к системе и генерировать метрики, так что в любой данный момент существует целый ряд пользователей в системе, которая ниже общей. Общее количество пользователей составляет около 1 тыс., И в сети их всегда бывает несколько сотен, что означает, что в таблице показателей есть несколько сотен строк с одинаковой меткой времени. Когда я создаю график, я не хочу показывать серию для каждого отдельного пользователя, потому что тогда график нечитабелен из-за слишком малого размера холста, ограниченной цветовой палитры, слишком длинной легенды и т. Д. Поэтому я вместо этого генерирую график, где только ТОП-10 пользователей по метрическим значениям отображаются индивидуально, а все остальные агрегируются в одну черную область. Вот как я это делаю:

  1. Я получаю топ 10 из таблицы:

    выбрать RRDKeyID, ср (значение) в качестве сред от metric_table где тс между 't1' и 't2' группа по RRDKeyID порядка они средний по алфавиту предел 10

  2. Затем в Perl сгенерировать команду RRDtool, которая рисует график, где каждый пользователь (RRDKeyID) имеет собственный DEF, но только Defs пользователей из результатов SQL запросов нанесены непосредственно , все остальные Агрегатируется:

    CDEF: другие = 0, user11, ADDNAN, user12, ADDNAN, user13, ADDNAN, ..., userN

, а затем "AREA: другие # 000000: ДРУГОЕ: СТЕК" отображается на графике.

Теперь я работаю над следующей версией приложения, где RRDtool будет заменен на D3.js из-за изменений в схеме БД, поэтому мне нужен другой способ генерации данных моих топ10 + других. Я не хочу отправлять необработанные данные клиенту и выполнять обработку на их стороне, потому что наборы данных могут быть очень большими, и обработка может занять много времени и процессор, поэтому я предпочел бы сделать это на стороне сервера. Я подозреваю, что это может быть возможным, чтобы получить результаты, которые я хочу в следующем формате:

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 (значение) в течение заданного диапазона времени, а затем верхний запрос печатает результаты для имен пользователей, если они находятся в результатах подзапроса или иным образом добавляют их другим. Я не знаю, как выразить эту идею в SQL, поэтому я буду благодарен, если кто-то может предложить решение.

+1

В шаге 1, вы не хотите 'ORDER BY avg (value) DESC'? –

+0

@RickJames Конечно, мой плохой. Отредактировано. – mac13k

ответ

0

Как я понимаю, вы хотите

  1. Узнайте топ 10
  2. Получить данные для построения их
  3. Получить среднее остальное, чтобы сделать 11-й участок.

Что-то вроде:

$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, и исходный запрос нужно будет повторить.

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. Вы можете добавить другое имя, если хотите.

+0

Этот запрос почти делает это. Я не уверен, был ли я достаточно ясным, но я действительно хотел, чтобы временные ряды для каждой из 10-ти и других результатов были результатом, поэтому в каждой строке должно быть (ts, rrdkeyid, value). Запрос, который вы предоставили, может быть определенно использован для определения того, будет ли ключ иметь свою собственную серию или землю в других, но как насчет запроса, который создает серию? – mac13k

+0

@ mac13k. , , Я предполагаю, что это какая-то форма условной агрегации, но я просматриваю вопрос и не знаю, в какой форме вы хотите получить результат. –

+0

Редактирование сделано: нужный формат результатов был набросан. – mac13k

Смежные вопросы