2011-12-25 19 views
5

tengo un problema con esta consulta:Optimizar consulta de MySQL usando índices

SELECT DISTINCT s.city, pc.start, pc.end 
FROM postal_codes pc LEFT JOIN suspects s ON (s.postalcode BETWEEN pc.start AND  pc.end) 
WHERE pc.user_id = "username" 
ORDER BY pc.start 

mesa sospechoso tiene alrededor de 340 000 entradas, hay un índice en postalcode, tengo varios usuarios, pero esta consulta individual tiene aproximadamente 0,5 s, cuando ejecuto este SQL con Explain, obtengo algo como esto: http://my.jetscreenshot.com/7536/20111225-myhj-41kb.jpg - ¿significa esto que la consulta no utiliza el índice? El índice es un BTREE, así que creo que esto debería correr un poco más rápido.

¿Me pueden ayudar con esto? Si hay alguna otra información necesaria, solo avíseme.

Editar: Tengo índices en los códigos sospechosos.postalcode, postal_codes.start, postal_codes.end, postal_codes.user_id.

Básicamente lo que estoy tratando de lograr: Tengo una tabla donde cada ID de usuario tiene varios rangos CódPostal asignado, por lo que parece:

user_id | start | end 

de lo que tengo una tabla de sospechosos que cada sospechoso tiene una dirección (que contiene un código postal), por lo que en esta consulta estoy tratando de obtener el rango de código postal - inicio y final y también el nombre de la ciudad en este rango.

Espero que esto ayude.

+0

Hola Joseph, ¿puedes editar la pregunta y enumerar el/los índice/s original/es en cada tabla, incluyendo qué columna (s) están en cada uno y en qué orden? – TetonSig

+0

Además, ¿puede aclarar qué se supone que debe devolver la consulta? Me parece que para un usuario dado, le gustaría mostrar la ciudad relacionada con cualquier código postal sospechoso que se encuentre dentro de un rango asignado a ese usuario. Y también le gustaría ver en qué rango cae la ciudad. Lo pregunto porque saber lo que quiere nos permitirá saber qué opciones tenemos para estructurar la consulta. – TetonSig

+0

Hola, he editado la publicación, espero que haya información útil ahora. – Joseph

Respuesta

0

Está utilizando solo un índice, y no para los campos implicados en la unión. Trate de crear un índice para los campos de inicio y fin, o el uso de> = y < = en lugar de ENTRE

+0

Hola, los campos de inicio y final están indexados (lo siento, no lo mencioné), lo he intentado con <= > = pero tiene el mismo resultado. – Joseph

0

No es 100% seguro, pero this podría ser relevante:

Sometimes MySQL does not use an index, even if one is available. One circumstance under which this occurs is when the optimizer estimates that using the index would require MySQL to access a very large percentage of the rows in the table. (In this case, a table scan is likely to be much faster because it requires fewer seeks.) However, if such a query uses LIMIT to retrieve only some of the rows, MySQL uses an index anyway, because it can much more quickly find the few rows to return in the result.

Así que trate de pruebas con LIMIT, y si usa el índice, encontraste tu causa.

+0

Hola, traté de agregar LIMIT con valores bastante pequeños como 10 y no tuvo ningún efecto:/ – Joseph

0

Tengo que decir que estoy un poco confundido por su convención de nombres de tabla, esperaría que la tabla "sospechosa" tenga un user_id no el código postal, pero debe tener sus razones. Si deja esta consulta tal como está, puede agregar un índice en postal_code (star, end) para evitar el escaneo completo de la tabla.

+0

Hola, la tabla postal_codes contiene 3 campos: user_id, start, end, así que básicamente mantiene información acerca de varios rangos de códigos postales para el user_id especificado. – Joseph

+0

"Hola, ya tengo una clave principal que contiene user_id, start, end". el problema es que mysql solo puede usar el prefijo del índice. La identificación del usuario no está en la cláusula where por lo que su índice no se está utilizando. intente tener un índice que comience con (inicio, fin, ...). –

+0

Hola, he agregado los índices, pero igual resultado. – Joseph

2

Cuando se utiliza la combinación izquierda, se recogen todos los registros de la primera tabla en lugar de la selección en función del índice. Sugeriría usar una unión interna. Algo como en la consulta a continuación.

select distinct 
    s.city, 
    pc.start, 
    pc.end 
from postal_codes pc, suspect s 
where 
    s.postalcode between (select pc1.start, pc1.end from postal_code pc1 where pc1.user_id = "username") 
    and pc.user_id = "username" 
order by pc.start 
+0

Inner joined es generalmente más lento que left one, y SELECTs en subconsultas no usan ningún índice. Joseph, ¿lo has intentado? – zapadlo

+0

Gracias, me da un error: Operando debe contener 1 columna (s) Es probable que entre la parte? No estoy seguro de cómo reescribir esto. – Joseph

0

Creo que se puede reestructurar su consulta como la siguiente,

SELECT DISTINCT s.city, pc1.start, pc1.end FROM 
(SELECT pc.start and pc.end from postal_codes pc where pc.user_id = "username") as pc1, Suspect s 
WHERE s.postalcode BETWEEN pc1.start, pc1.end ORDER BY pc1.start 

su consulta no está recogiendo el índice en la tabla s debido a la izquierda y unirse a su condición entre. Tener un índice en su tabla no significa necesariamente que se usará en todas las consultas.

+0

Gracias, he utilizado su consulta, pero tardó el mismo tiempo:/SELECCIONE DISTINCT en la ciudad, pc1.start, pc1.end FROM (SELECCIONE pc.start, pc.end FROM postal_codes pc WHERE pc.user_id = "nombre de usuario") como pc1, sospecha s WHERE en el código POSTAL entre pc1.start AND pc1.end ORDER BY pc1.start – Joseph

+0

Joseph-time para evaluar la consulta también depende de lo que es la distribución de datos en la tabla. – codemaster

+0

¿Qué quieres decir exactamente? – Joseph

0

Probar FORCE INDEX.

+0

Hola, tanques para responder. He forzado el índice en la tabla de sospechosos (campo de código postal) pero sin efecto, traté de forzar los índices también en el inicio y el final en la tabla códigos postales, pero la consulta tomó 5 veces más. – Joseph

Cuestiones relacionadas