2012-06-20 23 views
9

estoy ejecutando una consulta que se parece a esteUtilizando un índice en MySQL JOIN con condición OR

SELECT parent.field, child.field 
FROM parent 
JOIN child ON (child.id = parent.id 
    OR child.id = parent.otherid) 

Ésta es sin embargo muy lento (alrededor de 100 mil registros, y se une a otras tablas en la versión real), pero a pesar de haber intentado índices en

parent.id (PRIMARY), 
parent.otherid, 
child.id (PRIMARY), 
and a composite index of parent.id and parent.otherid 

No consigo que MySQL use ninguno de esos índices al hacer esta unión.

leí que MySQL puede utilizar solamente un índice por unirse, pero no puede encontrar en cualquier lugar si se puede utilizar un índice compuesto cuando un JOIN contiene una condición OR.

¿Alguien aquí sabe si es posible hacer referencia a esta consulta en un índice? Si es así, ¿cómo?


mi solución

(SO no me deja responder a mi propia pregunta a continuación atm)

Un montón de ajustes y se le ocurrió una solución bastante decente, que conserva la capacidad para unirse y agregar otras tablas

SELECT parent.field, child.field 
FROM parent 
JOIN (
    SELECT parent.id as parentid, 
    # Prevents the need to union 
    IF(NOT ISNULL(parent.otherid) AND parent.otherid <> parent.id, 
     parent.otherid, 
     parent.id) as getdataforid 
    FROM parent 
    WHERE (condition) 
) as foundrecords 
    ON foundrecords.parentid = parent.id 
JOIN child ON child.id = parent.getdataforid 

Para la velocidad requiere una condición dentro de la subconsulta para reducir el número de registros colocados en una tabla temporal, pero tengo toneladas de uniones adicionales en la consulta externa, algunos de unirse a los niños y otros para los padres (con algunos agregados) por lo que este funcionó mejor para mí.

En muchos casos, una unión será más rápida y efectiva, pero como estoy filtrando en mi padre, pero quiero datos adicionales de mi hijo (autorreferencias de padres), la unión me generó filas adicionales que no pude consolidar. Es posible encontrar el mismo resultado simplemente uniendo parent a sí mismo y aliasing una condición where en la consulta externa, pero este funciona muy bien para mí.

Gracias a Jirka para la UNION ALL de sugerencia, que es lo que me impulsó a llegar hasta aquí :)

+0

corriste '' EXPLAIN SELECT ... en la consulta? –

+1

sí, la explicación es lo que me permite saber que no está usando ningún índice para esta unión. –

Respuesta

7

Su consulta hace que sea teóricamente posible que un solo niño tiene dos padres distintos, lo que haría que la terminología bastante estándar. Sin embargo, asumamos que sus patrones de datos lo hacen imposible.

Luego, a continuación, obtendrá el mismo resultado utilizando índices separados, un índice por columna.

SELECT parent.field, child.field 
FROM parent 
JOIN child ON child.id = parent.id 

UNION ALL 

SELECT parent.field, child.field 
FROM parent 
JOIN child ON child.id = parent.otherid 
+0

+1. Excatly lo que yo sugeriría intentar, dividiendo esto en dos consultas separadas, y combinando los resultados, aunque esta consulta PODRÍA devolver más filas que la consulta original. (Considere una fila en el padre donde id = otherid). Para garantizar los mismos resultados, agregue el predicado 'AND parent.otherid <> parent.id' en esa segunda consulta. (Los valores NULL no son un problema en esa prueba, ya que parent.id NO ES NULO por ser PRIMARY KEY). – spencer7593

+1

Esta es una excelente solución en el caso simplificado (gracias), pero evita la agregación de datos en el niño (utilizando GROUP_CONCAT (DISTINCT child.field)). Jugando con la idea, sin embargo, para ver si puedo aplicarlo para acelerar consultas similares pero sin la agregación. Gracias: D –

+2

@BobDavies - Puedes. Incrustar esto como una subconsulta. –

0
EXPLAIN 
SELECT parent.fld, child.fld 
    FROM parent JOIN child ON child.id = parent.id 
UNION ALL 
SELECT parent.fld, child.fld 
    FROM parent JOIN child ON child.id = parent.otherid 
    AND parent.otherid <> parent.id 

con tablas usando el motor MyISAM:

id select_type TABLE  TYPE possible_keys KEY  key_len ref     ROWS Extra 
1 PRIMARY  parent  ALL  PRIMARY            9999 
1 PRIMARY  child  eq_ref PRIMARY  PRIMARY 4  test.parent.id  1 
2 UNION   parent  ALL  parent_ix1           9999 USING WHERE 
2 UNION   child  eq_ref PRIMARY  PRIMARY 4  test.parent.otherid 1 
    UNION RESULT <union1,2> ALL 

con tablas utilizando motor InnoDB:

id select_type table  type possible_keys key   key_len ref   rows Extra 
1 PRIMARY  child  ALL  PRIMARY           9903 
1 PRIMARY  parent  eq_ref PRIMARY  PRIMARY  4  test.child.id 1 
2 UNION   child  ALL  PRIMARY           9903 
2 UNION   parent  ref  parent_ix1  parent_ix1 5  test.child.id 1  Using where 
    UNION RESULT <union1,2> ALL