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_keywordtabela:
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_idemck 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_idemca1 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_idemck JOIN ca0eca0 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_identradas no índice sem primeiro conhecer oarticle_id. Sugiro que você crie um novo índice que tenhakeyword_idcomo 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.)