2010-07-26 5 views
3

Voici un jeu de réflexion pour vous:Limit MySQL, groupe et AVG Interrogation

Je garde les statistiques de trucs de calcul du cluster dans une table MySQL nommé « emploi ». Chaque ligne de travail a un hôte sur lequel le travail est exécuté (pas unique), un temps d'exécution du travail en secondes et un entier unique en tant que PK afin que je puisse commander les travaux terminés simplement en commandant le PK.

À l'heure actuelle, en utilisant la moyenne et le groupe par, je peux trouver le temps d'exécution moyen en secondes pour chaque hôte sur tous les travaux terminés. Au lieu de faire la moyenne de tous les temps d'exécution par hôte, je veux le temps moyen des cinq derniers emplois par hôte.

Il y a toutes sortes d'exemples d'opérations et de grouper par, et beaucoup d'exemples d'opérations avec limite, mais est-il possible de combiner les deux dans une requête MySQL assez simple?

EDIT: Dans le cas où je ne suis pas clair à ce sujet, je veux les cinq temps d'exécution moyen pour l'hôte 1, et les cinq temps d'exécution moyen pour l'hôte 2, etc.

Répondre

2

Ma première réaction était de limite d'utilisation pour limiter la moyenne à 5 résultats, ce qui m'a amené à proposer:

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; 

Mais il est clair que cela limite la moyenne des plus récents 5 emplois, et non les plus récents 5 emplois par hôte.

Il semble difficile d'utiliser LIMIT pour restreindre la moyenne sans utiliser de procédure stockée. Cela m'a conduit à envisager d'affecter à chaque tâche un ordre d'exécution par hôte, ou position, en utilisant une variable mysql.

Ce n'est pas testé, mais la théorie, il illustre devrait être un bon point de départ:

D'abord, nous devons attribuer à chaque tâche une position en fonction de son hôte:

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; 

Après avoir établi la position, il suffit de sélectionner la fonction d'agrégation, ce qui limite les résultats aux 5 premières positions:

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; 

S'il vous plaît laissez-moi savoir si cela fonctionne pour vous, ou s'il y a plus d'un spects je n'ai pas considéré. C'est un problème intrigant.

+0

Un petit problème; vous rencontrez le même problème que moi, la requête imbriquée se traduira par seulement cinq hôtes retournés. – Rob

+0

Oui, je vois que le problème était plus compliqué que je ne le pensais au départ. J'ai mis à jour ma solution pour tenter de résoudre ce problème. –

+0

Bien! Fonctionne parfaitement pour moi. – Rob

1

Je veux que les cinq temps d'exécution moyen pour l'hôte 1, et les cinq temps d'exécution moyen pour l'hôte 2, etc.

Oh ... Dans ce cas, utilisez:

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 ne dispose pas de fonctionnalités de classement/d'analyse/de fenêtrage, mais prend en charge les variables afin que vous puissiez obtenir la même fonctionnalité au ROW_NUMBER() OVER (PARTITION BY host ORDER BY execution_time DESC).

Auparavant:

 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

Je n'ai pas eu de chance avec ça non plus. Je n'ai qu'un seul résultat, et ce n'est pas la moyenne. – Rob

+0

@Rob: Mise à jour à la lumière de la clarification –

+0

Hmm ... J'ai eu quelques problèmes avec la solution ci-dessus jusqu'à présent, je vais probablement jouer avec un peu plus, cependant. – Rob