2011-11-21 12 views
11

He investigado esto, pero todavía no puede explicar por qué:Por qué es MySQL consulta, combinación izquierda 'considerablemente' más rápido que mi combinación interna

SELECT cl.`cl_boolean`, l.`l_name` 
FROM `card_legality` cl 
INNER JOIN `legality` l ON l.`legality_id` = cl.`legality_id` 
WHERE cl.`card_id` = 23155 

es significativamente más lento que:

SELECT cl.`cl_boolean`, l.`l_name` 
FROM `card_legality` cl 
LEFT JOIN `legality` l ON l.`legality_id` = cl.`legality_id` 
WHERE cl.`card_id` = 23155 

115 ms Vs 478 ms. Ambos usan InnoDB y hay relaciones definidas. La 'card_legality' contiene aproximadamente 200k filas, mientras que la tabla de 'legalidad' contiene 11 filas. Aquí está la estructura para cada uno:

CREATE TABLE `card_legality` (
    `card_id` varchar(8) NOT NULL DEFAULT '', 
    `legality_id` int(3) NOT NULL, 
    `cl_boolean` tinyint(1) NOT NULL, 
    PRIMARY KEY (`card_id`,`legality_id`), 
    KEY `legality_id` (`legality_id`), 
    CONSTRAINT `card_legality_ibfk_2` FOREIGN KEY (`legality_id`) REFERENCES `legality` (`legality_id`), 
    CONSTRAINT `card_legality_ibfk_1` FOREIGN KEY (`card_id`) REFERENCES `card` (`card_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 

Y:

CREATE TABLE `legality` (
    `legality_id` int(3) NOT NULL AUTO_INCREMENT, 
    `l_name` varchar(16) NOT NULL DEFAULT '', 
    PRIMARY KEY (`legality_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=latin1; 

yo podría simplemente utilizar IZQUIERDA-JOIN, pero no parece del todo bien ... alguna idea, por favor?

ACTUALIZACIÓN: Según lo solicitado, he incluido los resultados de explicar cada uno. Me había quedado previamente, pero yo no pretendo tener un conocimiento profundo de la misma ..

id select_type table type possible_keys key key_len ref rows Extra 
1 SIMPLE cl ALL PRIMARY NULL NULL NULL 199747 Using where 
1 SIMPLE l eq_ref PRIMARY PRIMARY 4 hexproof.co.uk.cl.legality_id 1 

Y, unirse interno:

id select_type table type possible_keys key key_len   ref       rows Extra 
1 SIMPLE l ALL PRIMARY NULL NULL NULL 11 
1 SIMPLE cl ref PRIMARY,legality_id legality_id 4 hexproof.co.uk.l.legality_id 33799 Using where 
+0

Por cierto 'card_id' es un VARCHAR ya que no tengo otra opción, normalmente no lo aceptaría. – Ben

Respuesta

9

Es debido a varchar en card_id. MySQL no puede usar el índice en card_id como card_id como se describe aquí mysql type conversion. La parte importante es

Para las comparaciones de una columna de cadena con un número, MySQL no puede usar un índice en la columna para buscar el valor rápidamente. Si str_col es una columna de cadena indexada , el índice no se puede utilizar cuando se realiza la búsqueda en la siguiente afirmación:

SELECT * FROM nombre_tabla DONDE str_col = 1;

La razón de esto es que hay muchas cadenas diferentes que pueden convertir al valor 1, como '1', '1' o '1a'.

Si cambia sus consultas a

SELECT cl.`cl_boolean`, l.`l_name` 
FROM `card_legality` cl 
INNER JOIN `legality` l ON l.`legality_id` = cl.`legality_id` 
WHERE cl.`card_id` = '23155' 

y

SELECT cl.`cl_boolean`, l.`l_name` 
FROM `card_legality` cl 
LEFT JOIN `legality` l ON l.`legality_id` = cl.`legality_id` 
WHERE cl.`card_id` = '23155' 

debería ver una gran mejora en la velocidad y también ver una diferente de explicar.

Aquí está una prueba similar (pero más fácil) para mostrar esto:

> desc id_test; 
+-------+------------+------+-----+---------+-------+ 
| Field | Type  | Null | Key | Default | Extra | 
+-------+------------+------+-----+---------+-------+ 
| id | varchar(8) | NO | PRI | NULL |  | 
+-------+------------+------+-----+---------+-------+ 
1 row in set (0.17 sec) 

> select * from id_test; 
+----+ 
| id | 
+----+ 
| 1 | 
| 2 | 
| 3 | 
| 4 | 
| 5 | 
| 6 | 
| 7 | 
| 8 | 
| 9 | 
+----+ 
9 rows in set (0.00 sec) 

> explain select * from id_test where id = 1; 
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra     | 
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+ 
| 1 | SIMPLE  | id_test | index | PRIMARY  | PRIMARY | 10  | NULL | 9 | Using where; Using index | 
+----+-------------+---------+-------+---------------+---------+---------+------+------+--------------------------+ 
1 row in set (0.00 sec) 


> explain select * from id_test where id = '1'; 
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+ 
| 1 | SIMPLE  | id_test | const | PRIMARY  | PRIMARY | 10  | const | 1 | Using index | 
+----+-------------+---------+-------+---------------+---------+---------+-------+------+-------------+ 
1 row in set (0.00 sec) 

En el primer caso hay Using where; Using index y el segundo es Using index. También ref es NULL o CONST. No hace falta decir que el segundo es mejor.

+0

aha! ¡Brillante! Estás en lo cierto, buscando en el 'card_id' como INT, cuando en realidad era un VARCHAR que hacía que estas consultas fueran casi 600 veces más lentas de lo que deberían haber sido. Gracias Andreas :) – Ben

+0

Sí. Muy buena información aquí gracias Andreas. – stefgosselin

+0

He agregado un enlace donde se explica esto. Es una buena lectura. –

3

que iba a tratar EXPLAIN tanto de las consultas. Simplemente prefija cada SELECT con EXPLAIN y ejecútelos. Proporciona información realmente útil sobre cómo mySQL está optimizando y ejecutando consultas.

+3

Basado en el tema en cuestión, diría que hay una gran posibilidad de que el OP sepa cómo usar "EXPLICAR". De cualquier forma, este tipo de información se debe incluir en un comentario y no en una respuesta ya que no está intentando responder su pregunta. – Naatan

+0

Hola L2G, gracias por tu comentario. Voy a perseverar con la función ** EXPLAIN ** y ver qué puedo encontrar. El manual de mysql es algo que falta (o al menos es para mí). Muchas gracias – Ben

0

Estoy bastante seguro de que MySql tiene una mejor optimización para Left Joins: no hay evidencia que respalde esto en este momento.

ETA: Una ronda explorador rápido y no puedo encontrar nada concreto para defender mi punto de vista tan .....

+0

Gracias K.Bob, leo similar, pero como usted; no encontró ninguna prueba. – Ben

2

L2G lo tiene más o menos resume, aunque sospecho que podría ser debido a la varchar tipo utilizado para card_id.

De hecho imprimí this informative page para benchmarking/profiling quickies. Aquí hay una técnica rápida de perfiles de pobres:

Time a SQL on MySQL 
Enable Profiling 
mysql> SET PROFILING = 1 
... 
RUN your SQLs 
... 
mysql> SHOW PROFILES; 

+----------+------------+-----------------------+ 
| Query_ID | Duration | Query     | 
+----------+------------+-----------------------+ 
|  1 | 0.00014600 | SELECT DATABASE()  | 
|  2 | 0.00024250 | select user from user | 
+----------+------------+-----------------------+ 
mysql> SHOW PROFILE for QUERY 2; 

+--------------------------------+----------+ 
| Status       | Duration | 
+--------------------------------+----------+ 
| starting      | 0.000034 | 
| checking query cache for query | 0.000033 | 
| checking permissions   | 0.000006 | 
| Opening tables     | 0.000011 | 
| init       | 0.000013 | 
| optimizing      | 0.000004 | 
| executing      | 0.000011 | 
| end       | 0.000004 | 
| query end      | 0.000002 | 
| freeing items     | 0.000026 | 
| logging slow query    | 0.000002 | 
| cleaning up     | 0.000003 | 
+--------------------------------+----------+ 

¡Buena suerte, ay, por favor, publique sus hallazgos!

+0

Esto es realmente información útil stefgosselin, gracias. Su pronóstico fue en realidad correcto, pero la explicación de Andreas lo explicó. Gracias por su ayuda :) – Ben

Cuestiones relacionadas