sql >> Databasteknik >  >> RDS >> Mysql

Hur kan jag undvika en fullständig tabellsökning på den här mysql-frågan?

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.



  1. Varför ska vi ha en ID-kolumn i användartabellen?

  2. Vad betyder det att "normalisera" en databas?

  3. Kan inte skapa enhetsdatamodell - med MySql och EF6

  4. Hur lagrar man flera alternativ i en enda tabell?