Гледайки вашето EXPLAIN изход, бях загрижен, че използването на подзаявки е довело до неоптимално използване на индекси. Почувствах (без никаква обосновка - и в това отношение може и да греша), че пренаписването чрез JOIN може да доведе до по-оптимизирана заявка.
За да направим това, трябва да разберем какво е предназначението на вашето запитване. Щеше да помогне, ако въпросът ви го беше формулирал, но след малко чесане по главата реших, че вашата заявка се опитва да извлече списък с всички други ключови думи, които се появяват във всяка статия, която съдържа дадена ключова дума, заедно с брой на всички статии, в които се появяват тези ключови думи .
Сега нека възстановим заявката на етапи:
-
Извличане на "всяка статия, която съдържа дадена ключова дума “ (без да се притеснявате за дубликати):
SELECT ca2.article_id FROM career_article_keyword AS ca2 WHERE ca2.keyword_id = 9; -
Извлечете „всички други ключови думи, които се появяват в [горе] "
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; -
Извлечете "[посоченото по-горе], заедно с броя на всички статии, в които се появяват тези ключови думи "
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; -
И накрая, искаме да добавим към изхода самата съответстваща ключова дума от
career_keywordтаблица: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;
Едно нещо, което веднага става ясно, е, че първоначалната ви заявка препраща към career_keywords два пъти, докато тази пренаписана заявка препраща към тази таблица само веднъж; само това може да обясни разликата в производителността - опитайте да премахнете втората препратка към него (т.е. където се появява в първата ви подзаявка), тъй като там е напълно излишно.
Поглеждайки назад към тази заявка, можем да видим, че се извършват съединявания на следните колони:
-
career_keywords.keyword_idвck JOIN ca0Тази таблица дефинира
PRIMARY KEY (`keyword_id`), така че има добър индекс, който може да се използва за това присъединяване. -
career_article_keyword.article_idвca1 JOIN ca2Тази таблица дефинира
UNIQUE KEY `article_id` (`article_id`,`keyword_id`)и тъй катоarticle_idе най-лявата колона в този индекс, има добър индекс, който може да се използва за това обединяване. -
career_article_keyword.keyword_idвck JOIN ca0иca0 JOIN ca1Няма индекс, който може да се използва за това обединяване:единственият индекс, дефиниран в тази таблица, има друга колона,
article_idвляво отkeyword_id- така че MySQL не може да намериkeyword_idзаписи в индекса, без първо да знаетеarticle_id. Предлагам ви да създадете нов индекс, който имаkeyword_idкато най-лявата му колона.(Необходимостта от този индекс също може да бъде установена директно от разглеждане на вашата оригинална заявка, където вашите две най-външни заявки извършват обединения на тази колона.)