2010-01-06 19 views
6

Tengo que UNIRSE a tablas grandes en una consulta MySQL y me toma mucho tiempo, aproximadamente 180 segundos. ¿Hay algún consejo para optimizar una combinación?aumentar la velocidad para MySQL JOIN para dos tablas grandes

Mi tabla tiene 10 campos. Solo estoy usando 4 en la consulta - todas las cadenas. La tabla tiene aproximadamente 600,000 filas y el resultado debe tener aproximadamente 50 filas.

Las cuatro filas usadas son: Título, variables, ubicación, fecha

Aquí está mi consulta:

SELECT DISTINCT t1.Title, t1.Variables FROM `MyTABLE` t1 JOIN `MyTABLE` t2 
USING (Title, Variables) 
WHERE (t1.Location, t1.Date) = ('Location1', 'Date1') 
AND (t2.Location, t2.Date) = ('Location2', 'Date2') 
+1

para empezar, utilice los índices – Lukman

+1

utilice "EXPLAIN" antes de su consulta para generar un plan de consulta que MySQL utilizará, que ayudará a la investigación de ayuda. – mjsabby

Respuesta

8

Al igual que otros señalados, necesita los índices adecuados. Para esta consulta en particular, puede beneficiarse de índices como:

(Location, Date) o (Date, Location) (para la cláusula WHERE) y (Title, Variables) o (Variables, Title) (para la condición join, ON cláusula)

Sería útil saber exactamente el tamaño (es decir, el tipo de datos) de la ubicación, fecha, título y columnas de variables, ya que un índice grande es más lento que uno pequeño.

Finalmente, solo un consejo: no usaría constructos de comparación sofisticados como usted. El

USING (Title, Variables) 

es probablemente muy bien, pero sin duda comprobar si

(t1.Location, t1.Date) = ('Location1', 'Date1') 

y

(t2.Location, t2.Forecast_date) = ('Location2', 'Date2') 

están comportando como se esperaba. Así que sin duda correr EXPLAIN en él, y comparar la salida con una comparación pasada de moda "regular", así:

t1.Location  = 'Location1' 
AND t1.Date   = 'Date1' 
AND t2.Location  = 'Location2' 
AND t2.Forecast_date = 'Date2' 

Se podría argumentar que, lógicamente, es el mismo y no debería importar - usted Estaría bien. Pero, una vez más, el optimizador de MySQL no es muy inteligente, y siempre existe la posibilidad de errores, especialmente con características que no se usan mucho. Creo que esta es una característica. Así que al menos trataría de EXPLICAR y ver si estas notaciones alternativas se evalúan de la misma manera.

Pero lo BenoKrapo señaló, ¿no sería más fácil hacer algo como esto:

SELECT Title, Variables 
FROM MyTABLE 
WHERE Location = 'Location1' AND Date = 'Date1' 
OR  Location = 'Location2' AND Date = 'Date2' 
GROUP BY Title, Variables 
HAVING COUNT(*) >= 2 

EDIT: He cambiado HAVING COUNT(*) = 2 a HAVING COUNT(*) >= 2. Ver comentarios (gracias de nuevo, BenoKrapo)

EDIT: días después de la publicación de esta respuesta, me encontré con este post de la marca Callaghan, MySQL Arquitecto de Facebook: http://www.facebook.com/note.php?note_id=243134480932 En esencia, se describe cómo entregan similares-pero-diferentes comparaciones 'inteligentes' rendimiento abismal debido al error del optimizador de MySQL. Así que mi punto es que, tratando de desentenderse de tu sintaxis cuando sufres, es posible que hayas tocado un error.

+0

Gracias por la cotización. De hecho, había pasado por alto la restricción de cardinalidad proveniente de la unión. Pero el contar (*) debe ser mayor o igual a 2, no igual a. –

+0

BenoKrapo: sí, tienes razón. Corregido eso, gracias! –

2

Sí. Cree índices apropiados en función de las consultas que se ejecutan en las tablas involucradas.

+0

En mi consulta, ya tengo índices en los campos utilizados en la lógica WHERE. ¿Hay algo más allá que pueda hacer? – Brian

+0

Lea la salida de 'EXPLAIN' y agregue índices basados ​​en eso. –

1

Asegúrate de que los campos con los que coincides están indexados. Los valores numéricos coincidentes también son más rápidos que las cadenas.

Pero ¿no sería más fácil simplemente escribir

SELECT DISTINCT 
    Title, 
    Variables 
FROM `MyTABLE` 
WHERE 
    Location = 'Location1' AND Date = 'Date1' 
    OR 
    Location = 'Location2' AND Date = 'Date2' 
+0

casi ... tienes que asegurarte de que vuelvan dos filas ... pero de hecho yo supervisé eso. Reñirte en mi anser –

2

¿Se puede anteponer la instrucción SQL con "explicar" y volver a ejecutarlo, lo más probable debido a los índices que faltan en las columnas que estás uniéndose en.

También intente utilizar STRAIGHT_JOIN y mencione la tabla que tiene un tamaño más lento en la izquierda, y la más grande a la derecha para indicar MySQL para elegir la primera tabla.

+2

También, ver qué tabla es menor (esto es ser ingenuo, pero aún así) y luego usar un STRAIGHT_JOIN a decir a MySQL para leer las tablas en orden (de izquierda a derecha) Por ejemplo: EXPLAIN SELECT tb1.X DE TB2 STRAIGHT_JOIN tb1 DONDE ... También de forma predeterminada se produce una UNIÓN INTERNA (que es un producto cartesiano), que puede ser lo que desee, pero es posible que vea si puede tener un OUTER JOIN – mjsabby

+0

mjsabby, está utilizando un autounión. por definición, son exactamente del mismo tamaño. En cuanto a INNER JOIN es un producto cartesiano: esto no tiene sentido. Si tiene un índice adecuado que puede usarse para resolver la operación de unión (en este caso, uno que tiene (Título, Variables)) MySQL ciertamente no calculará un producto cartesiano, pero usará una unión de bucle anidado. Finalmente, una unión externa probablemente empeoraría las cosas, en todo caso. –

+1

Me perdí el JOIN allí. – mjsabby

0

Sin la descripción de las tablas y la consulta, hay poco que podamos hacer para ayudar.

Existen varias cosas que pueden determinar la velocidad de una unión.

  • El motor de base de datos: ¿Está utilizando InnoDB o MyISAM? ¿O tal vez algún otro motor? Algunos son más rápidos en las búsquedas que otros, lo que afecta a las uniones.
  • Índices: ¿Están indexadas las columnas de coincidencia adecuadas?
  • Índices de partición: ¿Quizás pueda dividir la tabla por índices para hacerlo aún más rápido?

Además, mira EXPLAIN query que verá todos los pasos que mysql toma para ejecutarlo. Te podría ayudar muchísimo.

0

Intente utilizar el índice compuesto en las columnas en la cláusula where e intente poner todas las demás columnas en seleccionar en Columnas incluidas, esto ahorrará el costo tradicional de búsqueda.

1

Esto podría estar haciendo trampa un poco, pero en realidad me resultó más fácil UNIR las dos consultas juntas en PHP después de la consulta. Esto solo funciona porque estoy seleccionando dos variables distintas.

$query = "SELECT DISTINCT Title, Variables FROM 
MyTABLE WHERE Location='Location1' AND Variable='Variable1'"; 

$result = mysql_result($query); 
while ($row = mysql_array_assoc($result)) { 
    $Title = $row['Title']; 
    $Variables = $row['Variables']; 
    $Array_result1[$Title] = $Variables; 
} 


$query = "SELECT DISTINCT Title, Variables FROM 
MyTABLE WHERE Location='Location2' AND Variable='Variable2'"; 

$result = mysql_result($query); 
while ($row = mysql_array_assoc($result)) { 
    $Title = $row['Title']; 
    $Variables = $row['Variables']; 
    $Array_result2[$Title] = $Variables; 
} 

$Array_result = array_intersect($Array_result1, $Array_result2); 

Me gustaba la idea de utilizar únicamente una consulta MySQL para fusionar las dos consultas, pero esto es mucho más rápido.

0

Hice dos uniones separadas y un resultado combinado utilizando el operador de unión. Estaba obteniendo buenas mejoras a tiempo. SELECT t1.Title, t1.Variables FROM Mytable t1 JOIN Mytable t2 on (t1.Location, t1.Date) = ('Location1', 'Date1') UNION SELECT t1.Title, t1.Variables FROM Mytable t1 JOIN Mytable t2 on (t2.Location, t2.Date) = ('Location2', 'Date2');

Asegúrese de que tanto las consultas tienen el mismo número de la columna y el mismo tipo de datos para cada columna. Además, verifique el orden de la cláusula de selección.

Cuestiones relacionadas