Guardando il tuo EXPLAIN
output, ero preoccupato che l'uso delle sottoquery avesse comportato un uso non ottimale degli indici. mi sono sentito (senza alcuna giustificazione - e su questo potrei benissimo sbagliarmi) quella riscrittura usando JOIN
potrebbe portare a una query più ottimizzata.
Per fare ciò, dobbiamo capire a cosa serve la tua richiesta. Sarebbe stato d'aiuto se la tua domanda l'avesse articolata, ma dopo un piccolo grattacapo ho deciso che la tua query stava cercando di recuperare un elenco di tutte le altre parole chiave che appaiono in qualsiasi articolo che contiene una determinata parola chiave, insieme a un conteggio di tutti gli articoli in cui compaiono tali parole chiave .
Ora ricostruiamo la query in più fasi:
-
Recupera "qualsiasi articolo che contiene una determinata parola chiave " (senza preoccuparsi dei duplicati):
SELECT ca2.article_id FROM career_article_keyword AS ca2 WHERE ca2.keyword_id = 9;
-
Recupera "tutte le altre parole chiave che appaiono in [sopra] "
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;
-
Recupera "[quanto sopra], insieme a un conteggio di tutti gli articoli in cui compaiono quelle parole chiave "
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;
-
Infine, vogliamo aggiungere all'output la stessa parola chiave corrispondente da
career_keyword
tabella: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 che è immediatamente chiara è che la tua query originale faceva riferimento a career_keywords
due volte, mentre questa query riscritta fa riferimento a quella tabella solo una volta; questo da solo potrebbe spiegare la differenza di prestazioni:prova a rimuovere il secondo riferimento ad esso (ovvero dove appare nella tua prima sottoquery), poiché lì è del tutto ridondante.
Guardando indietro a questa query, possiamo vedere che i join vengono eseguiti sulle seguenti colonne:
-
career_keywords.keyword_id
inck JOIN ca0
Questa tabella definisce
PRIMARY KEY (`keyword_id`)
, quindi esiste un buon indice che può essere utilizzato per questo join. -
career_article_keyword.article_id
inca1 JOIN ca2
Questa tabella definisce
UNIQUE KEY `article_id` (`article_id`,`keyword_id`)
e, poichéarticle_id
è la colonna più a sinistra di questo indice, c'è un buon indice che può essere utilizzato per questo join. -
career_article_keyword.keyword_id
inck JOIN ca0
eca0 JOIN ca1
Non esiste alcun indice che può essere utilizzato per questo join:l'unico indice definito in questa tabella ha un'altra colonna,
article_id
a sinistra dikeyword_id
- quindi MySQL non riesce a trovarekeyword_id
voci nell'indice senza prima conoscere l'article_id
. Ti suggerisco di creare un nuovo indice che abbiakeyword_id
come colonna più a sinistra.(La necessità di questo indice potrebbe ugualmente essere stata accertata guardando la tua query originale, dove le tue due query più esterne eseguono join su quella colonna.)