2012-03-27 4 views
1

J'ai une telle requête:MySQL autojointure avec commande par l'optimisation

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 

EXPLIQUEZ me dit que cette requête utilise temporaire et filesort. Il court près de 10 sur la table avec 500k lignes. Comment cela peut-il être optimisé?

schéma:

enter image description here

Index:

enter image description here

Après des suggestions données par Joachim Isaksson, aucune amélioration de la performance, EXPLAIN:

enter image description here

+0

Quels indices avez-vous sur la table? –

+0

Je vais supposer qu'il y a une bonne raison pour laquelle la moyenne et le nombre ne sont pas calculés par les fonctions agrégées. – bernie

+0

C'est en fait une table de module de voteapi de Drupal (il y a une autre table de voteapi_vote qui contient tous les votes - environ 18 millions de lignes), qui contient déjà des résultats agrégés et mis en cache. – breethe

Répondre

2

Ma suggestion est de briser la jointure en deux requêtes ...

d'abord, construire un index sur la colonne function, value,

Votre première requête devrait obtenir la meilleure moyenne,
parce que c'est le premier valeur de tri,
tels que:

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; 

Ensuite, boucle à travers les 30 lignes pour obtenir le content_id,
et votre deuxième seconde requête est d'obtenir les 30 lignes de comptage pour chaque content_id,
qui est moyenne:

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

à travers les 2e résultats et de combiner avec le premier résultat pour obtenir la meilleure moyenne de 10 + nombre desc

cela peut éviter massif rejoindre

+0

Il évitera en effet la jointure, mais sachez que 30 est une sorte de "ajuster à une valeur suffisamment supérieure à 10 jusqu'à ce que cela fonctionne pour la limite de votre jeu de données". Si elle est trop basse, elle peut ne pas donner le même résultat que l'original. –

0

sur la base de la réponse de @ ajreal, vous pouvez le faire comme -

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;