2012-03-27 7 views
1

Ich habe eine solche Abfrage:MySQL Selbstverknüpfung mit, um durch Optimierung

SELECT va.value, vc.value 
FROM votingapi_cache va 
LEFT JOIN votingapi_cache vc ON vc.content_id = va.content_id 
WHERE va.content_type = 'node' AND va.value_type = 'percent' AND va.tag = 'vote' AND va.function = 'average' AND vc.content_type = 'node' AND vc.tag = 'vote' AND vc.function = 'count' 
ORDER BY va.value DESC, vc.value DESC LIMIT 0, 10 

ERKLÄREN sagt mir, dass diese Abfrage temporäre und filesort verwendet. Es läuft fast 10s auf dem Tisch mit 500k Zeilen. Wie kann es optimiert werden?

Schema:

enter image description here

Indizes:

enter image description here

Nach Vorschläge von Joachim Isaksson gegeben, keine Leistungsverbesserungen, ERKLÄREN:

enter image description here

+0

Welche Indizes haben Sie auf dem Tisch? –

+0

Ich gehe davon aus, dass es einen guten Grund gibt, warum Durchschnitt und Anzahl nicht durch Aggregatfunktionen berechnet werden. – bernie

+0

Es ist eigentlich eine Drupal-Votingapi-Modul-Tabelle (es gibt eine andere Tabelle votingapi_vote, die alle Stimmen enthält - etwa 18 Millionen Zeilen), die bereits aggregierte und zwischengespeicherte Ergebnisse enthält. – breethe

Antwort

2

ist mein Vorschlag die Verbindung in zwei Abfragen zu brechen ...

Zuerst einen Index auf Spalte function, value,

Ihre erste Abfrage den besten Durchschnitt bekommen sollte,
bauen, weil dies der erste ist Sortierwert,
wie:

SELECT average.value, average.content_id 
FROM votingapi_cache average 
WHERE average.function = 'average' /* plus other filter * 
ORDER BY average.value DESC LIMIT 0, 30; 

Dann Schleife durch die 30 Reihen der content_id zu erhalten,
und deine zweite zweiten Abfrage ist die 30 Reihen von Zahl für jede content_id zu bekommen,
, die mittlere ist:

select count.value, count.content_id 
FROM votingapi_cache `count` 
WHERE `count`.function = 'count' 
and content_id in(...30 content_id); 

Schleife durch den zweiten Treffer und mit dem ersten Ergebnis kombinieren beste zu bekommen 10 Durchschnitt + count ab

dies kann massiven

+0

Es wird in der Tat den Join vermeiden, aber hüte dich davor, dass 30 eine Art von 'Anpassung an einen Wert ausreichend über 10 ist, bis es für das Limit deines Datasets funktioniert. Wenn sie zu niedrig eingestellt ist, liefert sie möglicherweise nicht das gleiche Ergebnis wie das Original. –

0

Basierend auf @ ajreal Antwort beitreten vermeiden, können Sie es mögen könnte -

SELECT averages.value, counts.value 
FROM (
    SELECT * 
    FROM votingapi_cache 
    WHERE function = 'average' 
    AND content_type = 'node' 
    AND tag = 'vote' 
    AND value_type = 'percent' 
    ORDER BY value DESC 
    LIMIT 0, 30 
) AS averages 
LEFT JOIN votingapi_cache counts 
    ON averages.content_id = counts.content_id 
    AND averages.content_type = counts.content_type 
    AND averages.value_type = counts.value_type 
    AND averages.tag = counts.tag 
WHERE counts.function = 'count' 
ORDER BY averages.value DESC, counts.value DESC 
LIMIT 0, 10;