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_keyword
tabla: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_id
enck JOIN ca0
Esta 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_id
enca1 JOIN ca2
Esta tabla define
UNIQUE KEY `article_id` (`article_id`,`keyword_id`)
y, desdearticle_id
es 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_id
enck JOIN ca0
yca0 JOIN ca1
No hay ningún índice que se pueda usar para esta unión:el único índice definido en esta tabla tiene otra columna,
article_id
a la izquierda dekeyword_id
- por lo que MySQL no puede encontrarkeyword_id
entradas en el índice sin conocer primero elarticle_id
. Le sugiero que cree un nuevo índice que tengakeyword_id
como 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).