En regardant votre EXPLAIN
sortie, je craignais que votre utilisation des sous-requêtes ait entraîné une utilisation sous-optimale des index. J'ai ressenti (sans aucune justification - et là-dessus je peux très bien me tromper) que la réécriture en utilisant JOIN
peut conduire à une requête plus optimisée.
Pour ce faire, nous devons comprendre ce que votre requête est destinée à faire. Cela aurait aidé si votre question l'avait articulé, mais après un petit grattage, j'ai décidé que votre requête essayait de récupérer une liste de tous les autres mots-clés qui apparaissent dans n'importe quel article contenant un mot-clé donné, avec un nombre de tous les articles dans lesquels ces mots-clés apparaissent .
Reconstruisons maintenant la requête par étapes :
-
Récupérer "tout article contenant un mot-clé donné " (sans se soucier des doublons) :
SELECT ca2.article_id FROM career_article_keyword AS ca2 WHERE ca2.keyword_id = 9;
-
Récupérer "tous les autres mots clés qui apparaissent dans [ci-dessus] "
SELECT ca1.keyword_id FROM career_article_keyword AS ca1 JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id) WHERE ca1.keyword_id <> 9 AND ca2.keyword_id = 9 GROUP BY ca1.keyword_id;
-
Récupérer "[ce qui précède], ainsi que le nombre de tous les articles dans lesquels ces mots clés apparaissent "
SELECT ca1.keyword_id, COUNT(DISTINCT ca0.article_id) AS cnt FROM career_article_keyword AS ca0 JOIN career_article_keyword AS ca1 USING (keyword_id) JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id) WHERE ca1.keyword_id <> 9 AND ca2.keyword_id = 9 GROUP BY ca1.keyword_id ORDER BY cnt DESC;
-
Enfin, nous voulons ajouter à la sortie le mot-clé correspondant lui-même à partir du
career_keyword
tableau :SELECT ck.keyword_id, ck.keyword, COUNT(DISTINCT ca0.article_id) AS cnt FROM career_keywords AS ck JOIN career_article_keyword AS ca0 USING (keyword_id) JOIN career_article_keyword AS ca1 USING (keyword_id) JOIN career_article_keyword AS ca2 ON (ca2.article_id = ca1.article_id) WHERE ca1.keyword_id <> 9 AND ca2.keyword_id = 9 GROUP BY ck.keyword_id -- equal to ca1.keyword_id due to join conditions ORDER BY cnt DESC;
Une chose qui est immédiatement claire est que votre requête d'origine faisait référence à career_keywords
deux fois, alors que cette requête réécrite ne fait référence qu'une seule fois à cette table ; cela seul peut expliquer la différence de performances - essayez de supprimer la deuxième référence (c'est-à-dire là où elle apparaît dans votre première sous-requête), car elle est entièrement redondante.
En revenant sur cette requête, nous pouvons voir que des jointures sont effectuées sur les colonnes suivantes :
-
career_keywords.keyword_id
dansck JOIN ca0
Ce tableau définit la
PRIMARY KEY (`keyword_id`)
, il existe donc un bon index qui peut être utilisé pour cette jointure. -
career_article_keyword.article_id
dansca1 JOIN ca2
Ce tableau définit
UNIQUE KEY `article_id` (`article_id`,`keyword_id`)
et, depuisarticle_id
est la colonne la plus à gauche de cet index, il existe un bon index qui peut être utilisé pour cette jointure. -
career_article_keyword.keyword_id
dansck JOIN ca0
etca0 JOIN ca1
Aucun index ne peut être utilisé pour cette jointure :le seul index défini dans cette table a une autre colonne,
article_id
à gauche dekeyword_id
- donc MySQL ne peut pas trouverkeyword_id
entrées dans l'index sans connaître au préalable l'article_id
. Je vous suggère de créer un nouvel index qui akeyword_id
comme sa colonne la plus à gauche.(La nécessité de cet index aurait également pu être déterminée directement en examinant votre requête d'origine, où vos deux requêtes les plus externes effectuent des jointures sur cette colonne.)