sql >> Base de Datos >  >> RDS >> Mysql

¿Cómo puedo evitar un escaneo completo de la tabla en esta consulta mysql?

Basado en EXPLAIN salida en su pregunta, ya tiene todos los índices que la consulta debería estar usando, a saber:

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);

(No estoy seguro de sus nombres de índice si idx_zip_from_distance realmente incluye el zipcode_to columna. Si no, debe agregarlo para convertirlo en un índice de cobertura . Además, he incluido el venues.id columna en idx_zipcode para completar, pero, asumiendo que es la clave principal para la tabla y que está usando InnoDB, se incluirá automáticamente de todos modos).

Sin embargo, parece que MySQL está eligiendo un plan de consulta diferente, y posiblemente subóptimo, donde escanea todos los eventos, encuentra sus lugares y códigos postales, y solo luego filtra los resultados por distancia. Esto podría sería el plan de consulta óptimo, si la cardinalidad de la tabla de eventos fuera lo suficientemente baja, pero por el hecho de que está haciendo esta pregunta, asumo que no lo es.

Una razón para el plan de consulta subóptimo podría ser el hecho de que tienes demasiados índices que confunden al planificador. Por ejemplo, realmente ¿Necesita esos tres índices en la tabla de códigos postales, dado que los datos que almacena son presumiblemente simétricos? Personalmente, sugeriría solo el índice que describí anteriormente, más un índice único (que también puede ser la clave principal, si no tiene una artificial) en (zipcode_to, zipcode_from) (preferiblemente en ese orden, para que cualquier consulta ocasional sobre zipcode_to=? puede hacer uso de él).

Sin embargo, según algunas pruebas que hice, sospecho que el problema principal por el que MySQL elige el plan de consulta incorrecto se reduce simplemente a las cardinalidades relativas de sus tablas. Presumiblemente, sus zipcode_distances reales la mesa es enorme , y MySQL no es lo suficientemente inteligente como para darse cuenta de las condiciones en WHERE la cláusula realmente lo reduce.

Si es así, la solución mejor y más simple puede ser simplemente forzar MySQL para usar los índices que quieras :

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

Con esa consulta, debería obtener el plan de consulta deseado. (Necesitas FORCE INDEX aquí, ya que con solo USE INDEX el planificador de consultas aún podría decidir usar un escaneo de tabla en lugar del índice sugerido, anulando el propósito. Me pasó esto cuando probé esto por primera vez).

PD. Aquí hay una demostración de SQLize, tanto con y sin FORCE INDEX , demostrando el problema.