Mirando tu EXPLAIN salida, me preocupaba que su uso de subconsultas hubiera resultado en un uso subóptimo de índices. yo sentí (sin ninguna justificación, y en esto muy bien puedo estar equivocado) que reescribir usando JOIN podría conducir a una consulta más optimizada.
Para hacer eso, necesitamos entender cuál es la intención de su consulta. Hubiera sido útil si su pregunta la hubiera articulado, pero después de rascarme un poco la cabeza, decidí que su consulta estaba tratando de obtener una lista de todas las demás palabras clave que aparecen en cualquier artículo que contenga alguna palabra clave determinada, junto con un recuento de todos los artículos en los que aparecen esas palabras clave .
Ahora reconstruyamos la consulta por etapas:
-
Obtener "cualquier artículo que contenga alguna palabra clave determinada " (sin preocuparse por los duplicados):
SELECT ca2.article_id FROM career_article_keyword AS ca2 WHERE ca2.keyword_id = 9; -
Obtener "todas las demás palabras clave que aparecen en [lo anterior] "
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; -
Obtener "[lo anterior], junto con un recuento de todos los artículos en los que aparecen esas palabras clave "
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; -
Finalmente, queremos agregar a la salida la palabra clave correspondiente de
career_keywordtabla: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;
Una cosa que queda clara de inmediato es que su consulta original hacía referencia a career_keywords dos veces, mientras que esta consulta reescrita hace referencia a esa tabla solo una vez; esto solo podría explicar la diferencia de rendimiento:intente eliminar la segunda referencia (es decir, donde aparece en su primera subconsulta), ya que es completamente redundante allí.
Mirando hacia atrás en esta consulta, podemos ver que se están realizando uniones en las siguientes columnas:
-
career_keywords.keyword_idenck JOIN ca0Esta tabla define
PRIMARY KEY (`keyword_id`), por lo que hay un buen índice que se puede usar para esta combinación. -
career_article_keyword.article_idenca1 JOIN ca2Esta tabla define
UNIQUE KEY `article_id` (`article_id`,`keyword_id`)y, desdearticle_ides la columna más a la izquierda en este índice, hay un buen índice que se puede usar para esta combinación. -
career_article_keyword.keyword_idenck JOIN ca0yca0 JOIN ca1No hay ningún índice que se pueda usar para esta unión:el único índice definido en esta tabla tiene otra columna,
article_ida la izquierda dekeyword_id- por lo que MySQL no puede encontrarkeyword_identradas en el índice sin conocer primero elarticle_id. Le sugiero que cree un nuevo índice que tengakeyword_idcomo su columna más a la izquierda.(La necesidad de este índice también podría haberse determinado directamente al observar su consulta original, donde sus dos consultas más externas realizan uniones en esa columna).