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_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;
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_id
wck JOIN ca0
Ta 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_id
wca1 JOIN ca2
Ta tabela definiuje
UNIQUE KEY `article_id` (`article_id`,`keyword_id`)
i ponieważarticle_id
jest skrajną lewą kolumną w tym indeksie, istnieje dobry indeks, którego można użyć do tego połączenia. -
career_article_keyword.keyword_id
wck JOIN ca0
ica0 JOIN ca1
Nie ma indeksu, którego można użyć dla tego złączenia:jedyny indeks zdefiniowany w tej tabeli ma inną kolumnę,
article_id
na lewo odkeyword_id
- więc MySQL nie może znaleźćkeyword_id
wpisy w indeksie bez uprzedniej znajomościarticle_id
. Proponuję utworzyć nowy indeks, który makeyword_id
w 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).