Въз основа на EXPLAIN изход във вашия въпрос, вече имате всички индекси, които заявката трябва да използвате, а именно:
CREATE INDEX idx_zip_from_distance
ON zipcode_distances (zipcode_from, distance, zipcode_to);
CREATE INDEX idx_zipcode ON venues (zipcode, id);
CREATE INDEX idx_venue_id ON events (venue_id);
(Не съм сигурен от имената на вашия индекс дали idx_zip_from_distance наистина включва zipcode_to колона. Ако не, трябва да го добавите, за да го направите покриващ индекс
. Освен това включих venues.id колона в idx_zipcode за пълнота, но ако приемем, че това е първичният ключ за таблицата и че използвате InnoDB, той така или иначе ще бъде включен автоматично.)
Изглежда обаче, че MySQL избира различен и вероятно неоптимален план за заявка, където сканира всички събития, намира техните места и пощенски кодове и едва след това филтрира резултатите по разстояние. Това може бъде оптималният план за заявка, ако мощността на таблицата със събития е била достатъчно ниска, но от факта, че задавате този въпрос, предполагам, че не е така.
Една от причините за неоптималния план за заявка може бъде фактът, че имате твърде много индекси, които объркват плановицата. Например, наистина ли имате нужда и от трите индекса в таблицата с пощенски кодове, като се има предвид, че данните, които съхранява, вероятно са симетрични? Лично аз бих предложил само индекса, който описах по-горе, плюс уникален индекс (който може да бъде и първичен ключ, ако нямате изкуствен) на (zipcode_to, zipcode_from) (за предпочитане в този ред, така че всякакви случайни заявки към zipcode_to=? може да се възползва от него).
Въпреки това, въз основа на някои тестове, които направих, подозирам, че основният проблем, поради който MySQL избира грешен план за заявка, се свежда просто до относителните характеристики на вашите таблици. Вероятно вашите действителни zipcode_distances масата е огромна , а MySQL не е достатъчно умен, за да разбере колко много са условията в WHERE клаузата наистина го стеснява.
Ако е така, най-доброто и просто решение може да бъде просто принудително MySQL, за да използвате индексите, които искате :
select
*
from
zipcode_distances z
FORCE INDEX (idx_zip_from_distance)
inner join
venues v
FORCE INDEX (idx_zipcode)
on z.zipcode_to=v.zipcode
inner join
events e
FORCE INDEX (idx_venue_id)
on v.id=e.venue_id
where
z.zipcode_from='92108' and
z.distance <= 5
С тази заявка наистина трябва да получите желания план за заявка. (Необходим ви е FORCE INDEX тук, тъй като само с USE INDEX плановникът на заявки все пак може да реши да използва сканиране на таблица вместо предложения индекс, побеждавайки целта. Това ми се случи, когато за първи път тествах това.)
Пс. Ето демонстрация на SQLize, и двете с
и без
FORCE INDEX , демонстрирайки проблема.