Patrząc na swoje EXPLAIN dane wyjściowe, obawiałem się, że twoje użycie podzapytań spowodowało nieoptymalne użycie indeksów. czułem (bez żadnego uzasadnienia - i co do tego mogę się mylić), że przepisywanie za pomocą JOIN może prowadzić do bardziej zoptymalizowanego zapytania.
Aby to zrobić, musimy zrozumieć, do czego ma służyć Twoje zapytanie. Pomogłoby, gdyby Twoje pytanie zostało wyartykułowane, ale po krótkim zastanowieniu stwierdziłem, że Twoje zapytanie próbuje pobrać listę wszystkich innych słów kluczowych, które pojawiają się w każdym artykule zawierającym dane słowo kluczowe, wraz z liczbą wszystkich artykułów, w których pojawiają się te słowa kluczowe .
Teraz przebudujmy zapytanie etapami:
-
Pobierz „dowolny artykuł zawierający określone słowo kluczowe " (nie martw się o duplikaty):
SELECT ca2.article_id FROM career_article_keyword AS ca2 WHERE ca2.keyword_id = 9; -
Pobierz „wszystkie inne słowa kluczowe, które pojawiają się w [powyżej] "
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; -
Pobierz „[powyższe] wraz z liczbą wszystkich artykułów, w których pojawiają się te słowa kluczowe "
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; -
Na koniec chcemy dodać do wyniku pasujące słowo kluczowe z
career_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;
Jedno, co jest od razu jasne, to to, że oryginalne zapytanie odnosiło się do career_keywords dwukrotnie, podczas gdy to przepisane zapytanie odwołuje się do tej tabeli tylko raz; samo to może wyjaśniać różnicę w wydajności — spróbuj usunąć drugie odniesienie do niego (tj. miejsce, w którym pojawia się w pierwszym podzapytaniu), ponieważ jest tam całkowicie zbędne.
Patrząc wstecz na to zapytanie, widzimy, że połączenia są wykonywane w następujących kolumnach:
-
career_keywords.keyword_idwck JOIN ca0Ta tabela definiuje
PRIMARY KEY (`keyword_id`), więc istnieje dobry indeks, którego można użyć do tego połączenia. -
career_article_keyword.article_idwca1 JOIN ca2Ta tabela definiuje
UNIQUE KEY `article_id` (`article_id`,`keyword_id`)i ponieważarticle_idjest skrajną lewą kolumną w tym indeksie, istnieje dobry indeks, którego można użyć do tego połączenia. -
career_article_keyword.keyword_idwck JOIN ca0ica0 JOIN ca1Nie ma indeksu, którego można użyć dla tego złączenia:jedyny indeks zdefiniowany w tej tabeli ma inną kolumnę,
article_idna lewo odkeyword_id- więc MySQL nie może znaleźćkeyword_idwpisy w indeksie bez uprzedniej znajomościarticle_id. Proponuję utworzyć nowy indeks, który makeyword_idw skrajnej lewej kolumnie.(Zapotrzebowanie na ten indeks można było również ustalić bezpośrednio na podstawie oryginalnego zapytania, w którym dwa najbardziej zewnętrzne zapytania dokonują złączeń w tej kolumnie).