Въз основа на 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
, демонстрирайки проблема.