2010-07-26 2 views
3

Вот головоломка для вас:MySQL Limit, группы и AVG Query

Веду статы кластерной вычислительной вещи в таблице MySQL под названием «работа». Каждая строка задания имеет хост, выполняемое задание (не уникальное), время выполнения задания в секундах и уникальное целое число как ПК, поэтому я могу заказать завершенные задания, просто заказав ПК.

На данный момент, используя среднее значение и группу, я могу найти среднее время выполнения в секундах для каждого хоста по всем выполненным заданиям. Вместо того, чтобы усреднять все время выполнения на хост, я хочу среднее время последних пяти заданий на хост.

Существует множество примеров для операций и группировки, а также множество примеров для операций с лимитом, но есть ли способ сочетания этих двух в довольно простом запросе MySQL?

EDIT: В случае, если я не ясно об этом, я хочу, чтобы среднее время пять выполнения для хоста 1, а среднее время пять выполнения для хозяина 2 и т.д.

ответ

2

Моя первая реакция была использовать LIMIT ограничить среднем 5 результатов, что привело меня предположить:

select a.host, avg(a.execution_time) from (select id, execution_time, host from jobs order by id desc limit 5) a group by a.host; 

Но ясно, что это ограничивает среднем до самых последних 5 рабочих мест, а не самые последние 5 рабочих мест на хосте.

Кажется, трудно использовать LIMIT для ограничения среднего значения, не используя какую-либо хранимую процедуру. Это заставило меня рассмотреть возможность назначения каждого задания порядку завершения или хосту, используя переменную mysql.

Это проверялось, но теория она иллюстрирует должна быть хорошей отправной точкой:

Во-первых, мы должны назначить каждую работу позиции, основанной на его хозяина:

select 
    host, 
    execution_time, 
    @current_pos := if (@current_host = host, @current_pos, 0) + 1 as position, 
    @current_host := host 
from 
    (select @current_host := null, @current_pos := 0) set_pos, 
    jobs 
order by 
    host, 
    id desc; 

После установления положения, просто выбрать агрегатную функцию, ограничивая результаты топ-5 позиций:

select 
    jt.host, 
    avg(jt.execution_time) 
from 
    (
    select 
    host, 
    execution_time, 
    @current_pos := if (@current_host = host, @current_pos, 0) + 1 as position, 
    @current_host := host 
    from 
    (select @current_host := null, @current_pos := 0) set_pos, 
    jobs 
    order by 
    host, 
    id desc 
) jt 
where 
    jt.position <= 5 
group 
    by host; 

Пожалуйста, дайте мне знать, если это работает для вас, или если есть больше я не рассматривал. Это интригующая проблема.

+0

Небольшая проблема; вы столкнулись с той же проблемой, что и я, вложенный запрос приведет к возврату только пяти хостов. – Rob

+0

Да, я вижу, что проблема была сложнее, чем я изначально думал. Я обновил свое решение, пытаясь решить эту проблему. –

+0

Ницца! Прекрасно подходит для меня. – Rob

1

Я хочу, чтобы среднее время пять выполнения для хоста 1, а среднее время пять выполнения для хоста 2, и т.д.

О ... В этом случае, использование:

SELECT x.host, AVG(x.execution_time) 
    FROM (SELECT j.pk, 
       j.host, 
       j.execution_time, 
       CASE 
       WHEN @host != j.host THEN @rownum := 1 
       ELSE @rownum := @rownum + 1 
       END AS rank, 
       @host := j.host 
      FROM JOBS j 
      JOIN (SELECT @rownum := 0; @host := '') r 
     ORDER BY j.host, j.execution_time DESC) x 
WHERE x.rank <= 5 
GROUP BY x.host 

MySQL не имеет никакой функции ранжирования/аналитики/окон, но поддерживает переменные, поэтому вы можете получить ту же функциональность по адресу ROW_NUMBER() OVER (PARTITION BY host ORDER BY execution_time DESC).

Ранее:

 SELECT AVG(j.execution_time) AS avg_last_five_jobs 
     FROM JOBS j 
     JOIN (SELECT t.pk 
        FROM JOBS t 
      ORDER BY t.pk DESC 
        LIMIT 5) x ON x.pk = j.pk 
+0

Мне тоже не повезло. Я получаю только один результат, и это не средний показатель. – Rob

+0

@ Rob: Обновлено в свете осветления –

+0

Хм ... У меня было несколько проблем с вышеупомянутым решением, я, вероятно, поиграю с ним еще немного. – Rob