2008-10-09 16 views
15

He estado perfilando algunas consultas en una aplicación en la que estoy trabajando, y encontré una consulta que estaba recuperando más filas de las necesarias, y el conjunto de resultados se recortó en el código de la aplicación.Left Unirse para ver el logro Inner Join?

Al cambiar una UNIÓN IZQUIERDA a una UNIÓN INTERIOR se recortó el conjunto de resultados a justo lo que se necesitaba, y presumiblemente también sería más eficiente (ya que se seleccionaron menos filas). En realidad, la consulta LEFT JOIN'ed estaba superando a INNER JOIN'ed, tomando la mitad del tiempo para completarla.

LEFT JOIN: (127 filas totales, Consulta tomó 0,0011 seg)

INNER JOIN: (10 filas totales, Consulta tomó 0,0024 seg)

(I corrieron las consultas múltiples veces y aquellos son promedios) .

Correr explique en tanto no revela nada que explica las diferencias de rendimiento:

Para la combinación interna:

id select_type  table type possible_keys key  key_len  ref  rows  Extra 
1 SIMPLE contacts  index  NULL  name  302  NULL   235 Using where 
1 SIMPLE lists   eq_ref  PRIMARY  PRIMARY  4 contacts.list_id  1 
1 SIMPLE lists_to_users eq_ref  PRIMARY  PRIMARY  8 lists.id,const 1  
1 SIMPLE tags   eq_ref  PRIMARY  PRIMARY  4 lists_to_users.tag_id 1  
1 SIMPLE users   eq_ref  email_2  email_2  302  contacts.email 1 Using where 

Para la combinación izquierda:

id select_type  table type possible_keys key  key_len  ref  rows Extra 
1 SIMPLE   contacts index  NULL  name  302  NULL 235  Using where 
1 SIMPLE  lists  eq_ref  PRIMARY  PRIMARY  4 contacts.list_id 1  
1 SIMPLE lists_to_users eq_ref  PRIMARY  PRIMARY  8 lists.id,const 1  
1 SIMPLE   tags  eq_ref  PRIMARY  PRIMARY  4 lists_to_users.tag_id 1  
1 SIMPLE  users  eq_ref  email_2  email_2  302  contacts.email 1 

Y la propia consulta:

SELECT `contacts`.*, `lists`.`name` AS `group`, `lists`.`id` AS `group_id`, `lists`.`shared_yn`, `tags`.`name` AS `context`, `tags`.`id` AS `context_id`, `tags`.`color` AS `context_color`, `users`.`id` AS `user_id`, `users`.`avatar` 
FROM `contacts` 
LEFT JOIN `lists` ON lists.id=contacts.list_id 
LEFT JOIN `lists_to_users` ON lists_to_users.list_id=lists.id AND lists_to_users.user_id='1' AND lists_to_users.creator='1' 
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 
INNER JOIN `users` ON users.email=contacts.email 
WHERE (contacts.user_id='1') 
ORDER BY `contacts`.`name` ASC 

(La cláusula de la que estoy hablando es la ÚLTIMA UNIÓN INTERNA en la tabla 'usuarios')

La consulta se ejecuta en una base de datos MySQL 5.1, si hace una diferencia.

¿Alguien tiene una pista sobre por qué la consulta LEFT JOIN'ed supera a INNER JOIN'ed en este caso?

ACTUALIZACIÓN: Debido a la sugerencia de Tomalak que las pequeñas mesas que estoy usando estaban haciendo en la unión interna más compleja, que había creado una base de datos de prueba con algunos datos simulados. La tabla de 'usuarios' tiene 5000 filas y la tabla de contactos tiene ~ 500,000 filas. Los resultados son los mismos (también los tiempos no han cambiado, lo que es sorprendente si tenemos en cuenta que las tablas son mucho más grandes ahora).

También ejecuté ANALYZE y OPTIMIZE en la tabla de contactos. No hizo ninguna diferencia discernible.

+0

¿Intentó colocar primero la unión interna? –

+0

Tengo, acelera esa consulta en un 20%, pero aún más lenta que la UNIÓN IZQUIERDA –

+0

Intenta compilar cada consulta secuencialmente (unir una tabla, medir, unir la siguiente, etc.) Tal vez esto te ayude a determinar la operación lenta . – Tomalak

Respuesta

6

Probablemente se debe a que INNER JOIN tiene que verificar cada fila en ambas tablas para ver si los valores de columna (correo electrónico en su caso) coinciden. LEFT JOIN devolverá todo de una tabla independientemente. Si está indexado, sabrá qué hacer más rápido también.

+0

He intentado usar un índice en la columna de correo electrónico, y un índice combinado en las columnas nombre + correo electrónico, pero el plan de ejecución de consultas sigue siendo el mismo –

+0

Eso ayudará tanto a la IZQUIERDA como a la I Supongo, entonces no hubiera pensado que haría uno más rápido que el otro al hacerlo. – HAdes

+3

La unión interna escanea una tabla y encuentra filas coincidentes en la otra, idealmente usando e indexando para eso. No tiene que verificar cada fila en ambas tablas como sugiere. – Tomalak

4

Table cardinality tiene una influencia en el optimizador de consultas. Supongo que las tablas pequeñas que tiene hacen que la unión interna sea una operación más compleja. Tan pronto como tenga más registros de los que el servidor de bases de datos desea guardar en la memoria, la combinación interna probablemente superará a la combinación de la izquierda.

+0

Eso es interesante. Tendré que verificar un conjunto más grande y ver si funciona como lo describiste. –

+0

Volví a ejecutar con tablas mucho más grandes y los resultados son los mismos. –

+0

+1 en la respuesta . @ Eran Galperin he leído su nota sobre su pregunta y esas tablas de las que habla no son en absoluto "grandes". Con el hardware actual, necesita tablas con millones de filas, cuando hablamos de tablas grandes mate. – kommradHomer

2

imo está cayendo en la trampa conocida como optimización prematura. Los optimizadores de consultas son increíblemente inconstantes. Mi sugerencia es seguir adelante hasta que puedas identificar con certeza que una unión en particular es problemática.

+1

No se trata de optimización, se trata de entender por qué la consulta se comporta de cierta manera. –

-3

LEFT JOIN está devolviendo más filas que INNER JOIN porque estas 2 son diferentes.
Si LEFT JOIN no encuentra la entrada relacionada en la tabla que está buscando, devolverá valores NULL para la tabla.
Pero si INNER JOIN no encuentra la entrada relacionada, no devolverá la fila total .

Pero, para su pregunta, ¿tiene habilitada la función query_cache? intente ejecutar la consulta con

SELECT SQL_NO_CACHE `contacts`.*, ... 

Aparte de eso, me gustaría llenar las tablas con más datos, encontré

ANALYZE TABLE t1, t2; 
OPTIMIZE TABLE t1, t2; 

y ver qué pasa.

+0

Por supuesto, la combinación izquierda devuelve más filas, ese no es el punto de la pregunta. Por qué funciona más rápido MIENTRAS vuelvo más filas es lo que me asombra –

12

Si crees que la implementación de LEFT JOIN es INNER JOIN + más trabajo, entonces este resultado es confuso. ¿Qué ocurre si la implementación de INNER JOIN es (LEFT JOIN + filtering)? Ah, está claro ahora.

En los planes de consulta, la única diferencia es esta: usuarios ... extra: usando donde. Esto significa filtrar. Hay un filtro adicional paso en la consulta con la unión interna.


Este es un tipo diferente de filtrado de lo que se suele utilizar en una cláusula where. Es simple crear un índice en A para admitir esta acción de filtrado.

SELECT * 
FROM A 
WHERE A.ID = 3 

considerar esta consulta:

SELECT * 
FROM A 
    LEFT JOIN B 
    ON A.ID = B.ID 
WHERE B.ID is not null 

Esta consulta es equivalente a la combinación interna. No hay índice en B que ayude a esa acción de filtrado. La razón es que la cláusula donde está indicando una condición sobre el resultado de la unión, en lugar de una condición de B.

+0

Soy consciente de la diferencia entre una combinación izquierda y una combinación interna. Podría decir lo mismo sobre la cláusula WHERE, sin embargo, las consultas filtradas con una cláusula where generalmente toman mucho menos tiempo para computarse. –

+0

Leí lo que agregaste, y aunque creo que podrías estar enfocándote en algo con el paso de filtrado adicional, creo que no estás al tanto de por qué. Hay un índice en la columna de filtrado extra 'email' (que se usa), por lo que debe ser lo suficientemente rápido como para mejorar el rendimiento. –

+1

Sí, el índice en el correo electrónico ayuda a la izquierda a unirse. No, el índice en el correo electrónico no permite el filtrado rápido de los resultados de la unión posterior. –

0

Prueba esto:

SELECT `contacts`.*, `lists`.`name` AS `group`, `lists`.`id` AS `group_id`, `lists`.`shared_yn`, `tags`.`name` AS `context`, `tags`.`id` AS `context_id`, `tags`.`color` AS `context_color`, `users`.`id` AS `user_id`, `users`.`avatar` 
FROM `contacts` 
INNER JOIN `users` ON contacts.user_id='1' AND users.email=contacts.email 
LEFT JOIN `lists` ON lists.id=contacts.list_id 
LEFT JOIN `lists_to_users` ON lists_to_users.user_id='1' AND lists_to_users.creator='1' AND lists_to_users.list_id=lists.id 
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 
ORDER BY `contacts`.`name` ASC 

Eso debería darle una perfomance porque:

  • Coloque todas las uniones internas antes de que aparezca cualquier combinación "izquierda" o "derecha". Esto filtra algunos registros antes de aplicar las siguientes combinaciones externas
  • El cortocircuito de los operadores "Y" (el orden de los asuntos "Y"). Si el comparition entre las columnas y los literales es falsa, no se ejecutará la exploración de tabla necesario para el comparition entre las mesas y PK FKs

Si no encuentra ninguna mejora en el rendimiento, a continuación, reemplace todo el columnset para un "COUNT (*)" y realiza tus pruebas izquierda/interna. De esta manera, independientemente de la consulta, se recuperará sólo el 1 sola fila con 1 sola columna (la cuenta), por lo que se puede descartar que el número de bytes devuelto es la causa de la lentitud de su consulta:

SELECT COUNT(*) 
FROM `contacts` 
INNER JOIN `users` ON contacts.user_id='1' AND users.email=contacts.email 
LEFT JOIN `lists` ON lists.id=contacts.list_id 
LEFT JOIN `lists_to_users` ON lists_to_users.user_id='1' AND lists_to_users.creator='1' AND lists_to_users.list_id=lists.id 
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 

Buena suerte