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_keywordtableau :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_iddansck JOIN ca0Ce 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_iddansca1 JOIN ca2Ce tableau définit
UNIQUE KEY `article_id` (`article_id`,`keyword_id`)et, depuisarticle_idest la colonne la plus à gauche de cet index, il existe un bon index qui peut être utilisé pour cette jointure. -
career_article_keyword.keyword_iddansck JOIN ca0etca0 JOIN ca1Aucun 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_identré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_idcomme 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.)