2010-01-31 6 views
1

L'exécution d'un EXPLAIN sur certains de mes tests de requête ont abouti à ralentir toutes les jointures, même avec des index.
Comment rendre une requête MYSQL avec les informations suivantes plus efficace?Optimisation MYSQL (3) Table Join requête

Tables

contre: id (Pk), timestamp, user_id (fk)
utilisateur: id (PK), nom d'utilisateur, website_id (fk)
site: id (pk), nomdusite

SELECT t2.username, t3.sitename, count(*) as views FROM counter t1 
LEFT JOIN user t2 ON t2.id = t1.user_id 
LEFT JOIN website t3 ON t3.id = t2.website_id 
WHERE t1.id <> "" 
GROUP BY t1.id 
ORDER BY t1.id DESC 

Le résultat dans un tableau html:

nom d'utilisateur, nom du site, counter_views

+0

voir sql correct dans ma question éditer. – Hogan

Répondre

1

Vos données sont-elles encore pleines? Si ces tables sont vides, ou très petites, l'optimiseur peut choisir une requête "tout" parce que la table entière est sur une page. Le chargement d'une page à partir du disque pour obtenir la totalité de la table est plus rapide que celui d'une page sur le disque pour l'index, puis d'une autre page pour la page de données réelles.

+0

Les données sont relativement petites, le compteur 6000, les utilisateurs 100, les sites 10. – rrrfusco

+1

La table de compteur est probablement sur deux pages, mais même ainsi, le chargement de la table entière (2 pages) en mémoire a un coût égal dans les hits de page à la recherche d'index minimum. Donc, je ne suis pas surpris par les jointures «tout». Le comportement persiste-t-il même si vous augmentez la taille des tables, disons 100 fois? – TheDon

2

Ne pas count(*), utilisez plutôt count(t1.id)

Question Modifier le changement

Avec la question éditer, vous devez mettre un nom de colonne dans l'instruction count de ce que vous voulez compter, par exemple. Je crois que le sql est faux. Ne voulez-vous pas ceci:

select u.username, s.sitename, count(c.id) 
from user u 
join website s on u.website_id = s.id 
join counter c on u.id = c.user_id 
where u.id <> "" 
group by u.username, s.sitename 
order by u.id desc 
+0

Oui, dans une jointure, vous devez spécifier. –

+0

En fin de compte, je voudrais utiliser ORDER BY views [count (c.id) comme vues], mais EXPLAIN donne ALL join, clé NULL, en utilisant temporaire, en utilisant filesort. Y a-t-il un meilleur moyen? – rrrfusco

+0

@rrrfusco quels index avez-vous mis en place? – Hogan

0

Votre requête semble erronée. Je voudrais utiliser quelque chose comme ceci:

SELECT u.username, w.sitename, c.views 
FROM (
    SELECT user_id, COUNT(*) AS views FROM counter GROUP BY user_id 
) AS c 
LEFT JOIN user u ON u.id = c.user_id 
LEFT JOIN website w ON w.id = u.website_id 
ORDER BY c.views DESC 

J'ajouterais aussi l'index pour counter.user_id.

+0

Il existe un index sur user_id. J'ai toujours eu des problèmes avec les sous-sélections. phpmyadmin me dit que c.user_id est une colonne inconnue. ugh .. cela prendra un certain temps ... – rrrfusco

+0

Cette requête est fausse, si l'utilisateur a plus d'un site Web (comme le suggère la conception) alors cela ne fonctionnera pas.S'il existe une relation un-à-un dans le site Web de l'utilisateur, il existe une requête plus rapide qui utilise une jointure au lieu d'une sous-requête. (Les sous-requêtes sont souvent très très lentes à moins que le serveur ne les optimise). – Hogan

+0

La conception suggère que le site Web peut avoir quelques utilisateurs, j'ai fait une requête pour la conception. Eh bien, je suis d'accord que les sous-requêtes sont lentes. Je les utilise uniquement dans la section FROM s'il y a GROUP BY, car cela rend le code beaucoup plus lisible. Je n'aime pas ce quirk MySQL, si le champ n'est pas dans l'instruction GROUP BY, j'utiliserai la fonction d'agrégat. – Michas