2009-07-07 10 views
6

J'ai créé un forum personnalisé pour mon site en utilisant MySQL. La page de liste est essentiellement un tableau avec les colonnes suivantes: Rubrique, Dernière mise à jour et # Réponses.Optimisation de la requête MySQL pour éviter "Using where; Utilisation temporaire; Utilisation de filesort"

Le tableau DB a les colonnes suivantes:

id 
name 
body 
date 
topic_id 
email 

Un sujet a le topic_id de "0", et réponses ont le topic_id de leur sujet parent.

SELECT SQL_CALC_FOUND_ROWS 
    t.id, t.name, MAX(COALESCE(r.date, t.date)) AS date, COUNT(r.id) AS replies 
FROM 
    wp_pod_tbl_forum t 
LEFT OUTER JOIN 
    wp_pod_tbl_forum r ON (r.topic_id = t.id) 
WHERE 
    t.topic_id = 0 
GROUP BY 
    t.id 
ORDER BY 
    date DESC LIMIT 0,20; 

Il ya environ 2 100 éléments au total dans ce tableau, et les requêtes prennent généralement 6 secondes. J'ai ajouté un INDEX à la colonne "topic_id", mais cela n'a pas aidé beaucoup. Existe-t-il des moyens d'accélérer cette requête sans procéder à une restructuration importante?

EDIT: ne fonctionne pas encore. Je n'arrive pas à obtenir les exemples ci-dessous pour fonctionner correctement.

Répondre

7
SELECT id, name, last_reply, replies 
FROM (
     SELECT topic_id, MAX(date) AS last_reply, COUNT(*) AS replies 
     FROM wp_pod_tbl_forum 
     GROUP BY 
       topic_id 
     ) r 
JOIN wp_pod_tbl_forum t 
ON  t.topic_id = 0 
     AND t.id = r.topic_id 
UNION ALL 
SELECT id, name, date, 0 
FROM wp_pod_tbl_forum t 
WHERE NOT EXISTS 
     (
     SELECT NULL 
     FROM wp_pod_tbl_forum r 
     WHERE r.topic_id = t.id 
     ) 
     AND t.topic_id = 0 
ORDER BY 
     date DESC 
LIMIT 0, 20 

Si votre table est MyISAM ou id n'est pas un PRIMARY KEY, vous devez créer un ONDEX composite sur (topic_id, id).

Si votre table est InnoDB et id est un PRIMARY KEY, un index seulement sur (topic_id) fera (id sera implicitement ajouté à l'indice).

Mise à jour

Cette requête sera très probablement encore plus efficace, à condition que vous avez des index sur (topic_id, id) et (date, id):

Voir cet article dans mon blog pour plus de détails de performance:

Cette requête complète dans 30 ms sur les données de l'échantillon 100,000 rows:

SELECT id, name, last_reply, 
     (
     SELECT COUNT(*) 
     FROM wp_pod_tbl_forum fc 
     WHERE fc.topic_id = fl.topic_id 
     ) AS replies 
FROM (
     SELECT topic_id, date AS last_reply 
     FROM wp_pod_tbl_forum fo 
     WHERE id = (
       SELECT id 
       FROM wp_pod_tbl_forum fp 
       WHERE fp.topic_id = fo.topic_id 
       ORDER BY 
         fp.date DESC, fp.id DESC 
       LIMIT 1 
       ) 
       AND fo.topic_id <> 0 
     ORDER BY 
       fo.date DESC, fo.id DESC 
     LIMIT 20 
     ) fl 
JOIN wp_pod_tbl_forum ft 
ON  ft.id = fl.topic_id 
UNION ALL 
SELECT id, name, date, 0 
FROM wp_pod_tbl_forum t 
WHERE NOT EXISTS 
     (
     SELECT NULL 
     FROM wp_pod_tbl_forum r 
     WHERE r.topic_id = t.id 
     ) 
     AND t.topic_id = 0 
ORDER BY 
     last_reply DESC, id DESC 
LIMIT 20 

Les deux indices sont nécessaires pour cette requête pour être efficace.

Si votre table est InnoDBetid est un PRIMARY KEY, vous pouvez omettre id du indexes ci-dessus.

+0

La colonne 'date' dans la liste des champs est ambiguë. – Matt

+0

@Matt: voir mise à jour – Quassnoi

+0

@Quassnoi - pouvez-vous expliquer ce qui se passe? Est-ce que "UNION ALL" remplace "last_reply" par "date" si le sujet n'a pas de réponse? – Matt

1

Vous pouvez le décomposer en un ensemble de sous-requêtes (en tant que requêtes internes). Je dois le schéma pour jouer vraiment, mais si vous

SELECT t.id, t.name, MAX(COALESCE(r.date, t.date)) AS date, COUNT(r.id) AS replies 
FROM (
    SELECT (id, name, date) 
    FROM wp_pod_tbl_forum 
    WHERE topic_id = 0 
) as t 
LEFT OUTER JOIN 
    wp_pod_tbl_forum r 
WHERE 
    r.topic_id = t.id 
GROUP BY 
    t.id 
ORDER BY 
    date DESC LIMIT 0,20; 

qui peut aider à accélérer un peu, il peut même ne pas être la meilleure réponse (erreurs peuvent exister).

Il y a des tas de façons de le faire, mais la chose la plus importante à faire quand le réglage SQL est de réduire chaque ensemble autant que possible avant d'effectuer une opération.

Questions connexes