Гледайки вашето 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
като най-лявата му колона.(Необходимостта от този индекс също може да бъде установена директно от разглеждане на вашата оригинална заявка, където вашите две най-външни заявки извършват обединения на тази колона.)