2012-12-31 9 views
7
explain 
select 
    * 
from 
    zipcode_distances z 
inner join 
    venues v  
    on z.zipcode_to=v.zipcode 
inner join 
    events e 
    on v.id=e.venue_id 
where 
    z.zipcode_from='92108' and 
    z.distance <= 5 

Sto cercando di trovare tutti gli "eventi in luoghi entro 5 miglia di codice postale 92108", tuttavia, sto avendo difficoltà a ottimizzare questa query.Come posso evitare una scansione completa della tabella su questa query mysql?

Ecco ciò che il spiegano appare come segue:

id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra 

1, SIMPLE, e, ALL, idx_venue_id, , , , 60024, 
1, SIMPLE, v, eq_ref, PRIMARY,idx_zipcode, PRIMARY, 4, comedyworld.e.venue_id, 1, 
1, SIMPLE, z, ref, idx_zip_from_distance,idx_zip_to_distance,idx_zip_from_to, idx_zip_from_to, 30, const,comedyworld.v.zipcode, 1, Using where; Using index 

sto ottenendo una scansione completa della tabella sul "e" tavolo, e non riesco a capire cosa indice ho bisogno di creare per ottenerlo essere veloce

Tutto il consiglio sarebbe apprezzato

Grazie

+0

Avete bisogno di tutte le colonne di tutte le tabelle nel set di risultati? –

+0

Sto cercando di evitare di utilizzare una sottoquery "in". – john

+0

La descrizione colloquiale di ciò che sto cercando di fare è trovare i luoghi i cui codici zip si trovano nei codici postali che ho trovato in prossimità di 92108. Quindi si unisce alla sede e quindi si unisce agli eventi associati a quella sede . – john

risposta

7

Sulla base del EXPLAIN uscita nella sua interrogazione, si hanno già tutti gli indici della query dovrebbe essere utilizzato, vale a dire:

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

(non sono sicuro dai vostri nomi di indice se idx_zip_from_distance include davvero la colonna zipcode_to. In caso contrario, dovresti aggiungerla per renderla un covering index. Inoltre, ho incluso la colonna venues.id in idx_zipcode per completezza, ma, supponendo che sia la chiave primaria per la tabella e che stai usando InnoDB, sarà comunque incluso automaticamente)

Tuttavia, sembra che MySQL è la scelta di un diverso, e forse non ottimale, piano di query, dove si esplora attraverso tutti gli eventi, trova i luoghi e codici di avviamento postale, e solo allora filtra i risultati sulla distanza. Questo potrebbe essere il piano di query ottimale, se la cardinalità della tabella degli eventi era abbastanza basso, ma dal fatto che si sta chiedendo questa domanda presumo che non è.

Uno dei motivi per il piano di query non ottimale potrebbe essere il fatto che avete troppi indici che sono confondendo il pianificatore. Per esempio, pensi davvero bisogno di tutti e tre questi indici nella tabella codice postale, dato che i dati che memorizza è presumibilmente simmetrica? Personalmente, suggerirei solo l'indice che ho descritto sopra, più un indice univoco (che può anche essere la chiave primaria, se non si dispone di uno artificiale) sul (zipcode_to, zipcode_from) (preferibilmente in questo ordine, in modo che tutte le domande occasionali zipcode_to=? può farne uso).

Tuttavia, sulla base di alcuni test che ho fatto, ho il sospetto che il problema principale per cui MySQL è la scelta del piano di query sbagliato tratta semplicemente verso il basso ai relativi cardinalità delle tabelle. Presumibilmente, la vostra tabella effettiva zipcode_distances è enorme, e MySQL non è abbastanza intelligente per capire tutto quanto le condizioni della clausola di WHERE davvero restringere il campo.

Se è così, la migliore e più semplice soluzione può essere quella di semplicemente force MySQL to use the indexes you want:

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 tale query, si dovrebbe infatti ottenere il piano di query desiderata. (Si ha bisogno FORCE INDEX qui, dal momento che con solo USE INDEX il pianificatore di query potrebbe ancora decidere di utilizzare una scansione di tabella invece che l'indice suggerito, sconfiggendo lo scopo. Ho avuto questo accada quando ho provato prima questo.)

Ps. Ecco una demo su SQLize, sia with e withoutFORCE INDEX, a dimostrazione del problema.

0

Si potrebbe utilizzare una sottoquery:

select * from zipcode_distances z, venues v, events e 
where 
    z.id in (select id from zipcode z where z.zipcode_from='92108' and z.distance <= 5) 
    and z.zipcode_to=v.zipcode 
    and v.id=e.venue_id 
1

hanno indicizzato le colonne in entrambe le tabelle?

e.id and v.venue_id 

Se non lo fai, crea indici in entrambe le tabelle. Se lo hai già, potrebbe essere che tu abbia pochi record in una o più tabelle e analizzatore rilevi che è più efficiente eseguire una scansione completa piuttosto che una lettura indicizzata.

0

Si selezionano tutte le colonne da tutte le tabelle (select *) quindi l'ottimizzatore utilizza un indice poco importante quando il motore di query dovrà quindi effettuare una ricerca dall'indice alla tabella su ogni singola riga.

Problemi correlati