2009-05-26 9 views
1

Ich habe eine Tabelle, die das Auftreten von einer speziellen Aktion von verschiedenen Benutzern an verschiedenen Objekten zählt:MySQL: Abfrage der oberen n Aggregationen

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

Jedes Mal, wenn ein Benutzer diese Aktion ausführt, eine Zeile eingefügt wird. Ich kann zählen, wie viele Aktionen für jedes Objekt durchgeführt wurden, und um Objekte, die von ‚Tätigkeit‘:

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

Wie kann ich die Ergebnisse in den oberen n Objekte begrenzen? Die LIMIT-Klausel wird vor der Aggregation angewendet, was zu falschen Ergebnissen führt. Die Tabelle ist potentiell riesig (Millionen von Zeilen) und ich muss wahrscheinlich Dutzende Male pro Minute zählen, also möchte ich das so effizient wie möglich machen.

bearbeiten: Eigentlich hat Machine Recht, und ich war falsch mit der Zeit, bei der LIMIT angewendet wird. Meine Abfrage lieferte die korrekten Ergebnisse, aber die GUI, die sie mir präsentierte, warf mich ab ... diese Art macht diese Frage sinnlos. Es tut uns leid!

Antwort

2

Eigentlich ... LIMIT zuletzt nach einer eventuellen HAVING-Klausel angewandt wird. Also sollte es Ihnen keine falschen Ergebnisse geben. Da LIMIT jedoch zuletzt angewendet wird, bietet es keine schnellere Ausführung Ihrer Abfrage, da eine temporäre Tabelle in der Reihenfolge der Aktionsanzahl erstellt und sortiert werden muss, bevor das Ergebnis abgeschnitten wird. Denken Sie auch daran in absteigender Reihenfolge zu sortieren:

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

Sie könnten versuchen, einen Index object_id zur Optimierung hinzufügen. Auf diese Weise muss nur der Index gescannt werden anstelle der Aktionen Tabelle.

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

Wie wäre:

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

Auch, wenn Sie ein gewisses Maß an, was muss die Mindestanzahl von Aktionen zu enthalten sein (zB die Top-n diejenigen sind sicherlich mehr als 1000), Sie kann die Effizienz erhöhen, indem eine HAVING-Klausel hinzugefügt:

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

Ich weiß, dass dieser Thread 2 Jahre alt ist, aber Stackflow findet es immer noch relevant, hier geht meine $ 0,02. ORDER BY-Klauseln sind rechenintensiv und sollten daher in großen Tabellen vermieden werden. Ein Trick I (teilweise von Joe Celko SQL für Smarties) ist so etwas wie:

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 

Werden Sie die Top-15-bearbeiteten Objekte geben, ohne zu sortieren. Beachten Sie, dass mysql ab Version 5 nur Ergebnismengen für genau Duplikate (Whitespace incl.) Zwischenspeichert, sodass die verschachtelte Abfrage nicht zwischengespeichert wird. Das Verwenden einer Ansicht würde dieses Problem beheben.

Ja, es sind drei Abfragen statt zwei und und der einzige Vorteil ist, dass Sie die gruppierte Abfrage nicht sortieren müssen, aber wenn Sie viele Gruppen haben, wird es schneller.

Seitennotiz: Die Abfrage ist wirklich praktisch für Medianfunktionen ohne Sortierung