Olhando para o seu
EXPLAIN
saída, eu estava preocupado que seu uso de subconsultas resultou em um uso abaixo do ideal de índices. Eu senti (sem qualquer justificativa - e nisso eu posso muito bem estar errado) que reescrever usando JOIN
pode levar a uma consulta mais otimizada. Para fazer isso, precisamos entender o que sua consulta pretende fazer. Teria ajudado se sua pergunta tivesse articulado, mas depois de um pouco de coçar a cabeça, decidi que sua consulta estava tentando buscar uma lista de todas as outras palavras-chave que aparecem em qualquer artigo que contenha uma determinada palavra-chave, juntamente com uma contagem de todos os artigos em que essas palavras-chave aparecem .
Agora vamos reconstruir a consulta em etapas:
-
Buscar "qualquer artigo que contenha uma determinada palavra-chave " (sem se preocupar com duplicatas):
SELECT ca2.article_id FROM career_article_keyword AS ca2 WHERE ca2.keyword_id = 9;
-
Buscar "todas as outras palavras-chave que aparecem em [acima] "
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;
-
Buscar "[o acima], juntamente com uma contagem de todos os artigos em que essas palavras-chave aparecem "
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 adicionar à saída a palavra-chave correspondente dacareer_keyword
tabela:
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;
Uma coisa que fica imediatamente clara é que sua consulta original fazia referência a
career_keywords
duas vezes, enquanto essa consulta reescrita faz referência a essa tabela apenas uma vez; isso por si só pode explicar a diferença de desempenho - tente remover a segunda referência a ela (ou seja, onde ela aparece em sua primeira subconsulta), pois é totalmente redundante lá. Olhando para esta consulta, podemos ver que as junções estão sendo executadas nas seguintes colunas:
-
career_keywords.keyword_id
emck JOIN ca0
Esta tabela definePRIMARY KEY (`keyword_id`)
, portanto, há um bom índice que pode ser usado para essa junção.
-
career_article_keyword.article_id
emca1 JOIN ca2
Esta tabela defineUNIQUE KEY `article_id` (`article_id`,`keyword_id`)
e, comoarticle_id
é a coluna mais à esquerda neste índice, existe um bom índice que pode ser usado para esta junção.
-
career_article_keyword.keyword_id
emck JOIN ca0
eca0 JOIN ca1
Não há índice que possa ser usado para esta junção:o único índice definido nesta tabela tem outra coluna,article_id
à esquerda dekeyword_id
- então o MySQL não pode encontrarkeyword_id
entradas no índice sem primeiro conhecer oarticle_id
. Sugiro que você crie um novo índice que tenhakeyword_id
como sua coluna mais à esquerda.
(A necessidade desse índice também pode ter sido determinada diretamente ao examinar sua consulta original, onde suas duas consultas externas realizam junções nessa coluna.)