Baserat på EXPLAIN
output i din fråga har du redan alla index som frågan bör använder, nämligen:
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);
(Jag är inte säker på dina indexnamn om idx_zip_from_distance
innehåller verkligen zipcode_to
kolumn. Om inte, bör du lägga till det för att göra det till ett täckande index
. Jag har också inkluderat venues.id
kolumnen i idx_zipcode
för fullständighetens skull, men förutsatt att det är den primära nyckeln för tabellen och att du använder InnoDB, kommer den att inkluderas automatiskt ändå.)
Det ser dock ut som att MySQL väljer en annan, och möjligen suboptimal, frågeplan, där den skannar igenom alla evenemang, hittar deras lokaler och postnummer och först därefter filtrerar resultaten på distans. Detta kunde vara den optimala frågeplanen, om händelsetabellens kardinalitet var tillräckligt låg, men från det faktum att du ställer den här frågan antar jag att det inte är det.
En anledning till den suboptimala frågeplanen kan vara det faktum att du har för många index som förvirrar planeraren. Till exempel, gör du verkligen behöver alla tre av dessa index på postnummertabellen, med tanke på att data den lagrar förmodligen är symmetrisk? Personligen skulle jag bara föreslå indexet som jag beskrev ovan, plus ett unikt index (som också kan vara primärnyckeln, om du inte har en artificiell sådan) på (zipcode_to, zipcode_from)
(helst i den ordningen, så att eventuella enstaka frågor på zipcode_to=?
kan använda det).
Men baserat på några tester jag gjorde, misstänker jag att huvudproblemet varför MySQL väljer fel frågeplan helt enkelt beror på de relativa kardinaliteterna för dina tabeller. Förmodligen dina faktiska zipcode_distances
tabellen är stor , och MySQL är inte smart nog att inse hur mycket villkoren i WHERE
klausulen begränsar det verkligen.
Om så är fallet kan den bästa och enklaste lösningen vara att helt enkelt forcera MySQL för att använda de index du vill ha :
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
Med den frågan borde du verkligen få den önskade frågeplanen. (Du behöver FORCE INDEX
här, eftersom med bara USE INDEX
frågeplaneraren kan fortfarande välja att använda en tabellsökning istället för det föreslagna indexet, vilket motverkar syftet. Det här hände när jag testade det här första gången.)
Ps. Här är en demo på SQLize, både med
och utan
FORCE INDEX
, som visar problemet.