2009-05-26 6 views
1

J'ai une table qui compte les occurrences d'une action spécifique par différents utilisateurs sur différents objets:MySQL: requête agrégations top n

CREATE TABLE `Actions` (
    `object_id` int(10) unsigned NOT NULL, 
    `user_id` int(10) unsigned NOT NULL, 
    `actionTime` datetime 
); 

Chaque fois qu'un utilisateur effectue cette action, une ligne est insérée. Je peux compter combien d'actions ont été effectuées sur chaque objet, et des objets de commande par « activité »:

SELECT object_id, count(object_id) AS action_count 
FROM `Actions` 
GROUP BY object_id 
ORDER BY action_count; 

Comment puis-je limiter les résultats aux n objets top? La clause LIMIT est appliquée avant l'agrégation, ce qui entraîne des résultats erronés. La table est potentiellement énorme (des millions de lignes) et j'ai probablement besoin de compter des dizaines de fois par minute, donc je voudrais faire ceci aussi efficace que possible.

edit: En fait, Machine a raison, et je me suis trompé sur l'heure à laquelle la limite est appliquée. Ma requête a renvoyé les résultats corrects, mais l'interface graphique qui me les a présentés m'a décontenancée ... cela rend inutile cette question. Pardon!

Répondre

2

en fait ... LIMIT est appliquée dernière, après une éventuelle clause HAVING. Donc, cela ne devrait pas vous donner des résultats incorrects. Cependant, puisque LIMIT est appliqué en dernier, il ne permettra pas une exécution plus rapide de votre requête, car une table temporaire devra être créée et triée par ordre d'action avant de couper le résultat. En outre, n'oubliez pas de trier par ordre décroissant:

SELECT object_id, count(object_id) AS action_count 
FROM `Actions` 
GROUP BY object_id 
ORDER BY action_count DESC 
LIMIT 10; 

Vous pouvez essayer d'ajouter un index à object_id pour l'optimisation. De cette façon, seul l'index devra être analysé à la place du tableau Actions.

0
SELECT * FROM (SELECT object_id, count(object_id) AS action_count 
     FROM `Actions` 
     GROUP BY object_id 
     ORDER BY action_count) LIMIT 10; 
1

Que diriez-vous:

SELECT * FROM 
(
SELECT object_id, count(object_id) AS action_count 
FROM `Actions` 
GROUP BY object_id 
ORDER BY action_count 
) 
LIMIT 15 

De plus, si vous avez une certaine mesure de ce qui doit être le nombre minimum d'actions à inclure (par exemple les n ceux sont sûrement plus de 1000), vous peut augmenter l'efficacité en ajoutant une clause HAVING:

SELECT * FROM 
(
SELECT object_id, count(object_id) AS action_count 
FROM `Actions` 
GROUP BY object_id 
HAVING action_count > 1000 
ORDER BY action_count 
) 
LIMIT 15 
1

Je sais que ce fil a 2 ans mais stackflow le trouve toujours pertinent alors voici mon $ 0.02. Les clauses ORDER BY sont très coûteuses en termes de calcul, elles doivent donc être évitées dans les grandes tables. Une astuce je (en partie de SQL de Joe Celko pour Smarties) est quelque chose comme:

SELECT COUNT(*) AS counter, t0.object_id FROM (SELECT COUNT(*), actions.object_id FROM actions GROUP BY id) AS t0, (SELECT COUNT(*), actions.object_id FROM actions GROUP BY id) AS t1 WHERE t0.object_id < t1.object_id GROUP BY object_id HAVING counter < 15 

vous donnera les 15 objets édités sans tri. Notez qu'à partir de la version 5, mysql mettra uniquement en cache les ensembles de résultats pour les requêtes exactement dupliquées (whitespace incl) afin que la requête imbriquée ne soit pas mise en cache. Utiliser une vue résoudrait ce problème.

Oui, il y a trois requêtes au lieu de deux et le seul gain est de ne pas avoir à trier la requête groupée mais si vous avez beaucoup de groupes, ce sera plus rapide. Note: la requête est très pratique pour les fonctions médianes sans tri

Questions connexes