Pregunta

En MySQL 5.0.75-0ubuntu10.2 tengo un diseño de tabla fijo como ese:

Tabla padre con un ID Tabla parent2 con un ID Tabla children1 con un ID de padre

CREATE TABLE  `Parent` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(200) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB
CREATE TABLE  `Parent2` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(200) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB
CREATE TABLE  `Children1` (
  `id` int(11) NOT NULL auto_increment,
  `parentId` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `parent` (`parentId`)
) ENGINE=InnoDB

Un hijo tiene un padre en una de las tablas Parent o Parent2 . Cuando necesito un hijo, uso una consulta como esa:

select * from Children1 c 
inner join (
select id as parentId from Parent
union 
select id as parentId from Parent2
) p on p.parentId = c.parentId

Explicando los resultados de esta consulta:

+----+--------------+------------+-------+---------------+---------+---------+------+------+-----------------------------------------------------+
| id | select_type  | table      | type  | possible_keys | key     | key_len | ref  | rows | Extra                                               |
+----+--------------+------------+-------+---------------+---------+---------+------+------+-----------------------------------------------------+
|  1 | PRIMARY      | NULL       | NULL  | NULL          | NULL    | NULL    | NULL | NULL | Impossible WHERE noticed after reading const tables | 
|  2 | DERIVED      | Parent     | index | NULL          | PRIMARY | 4       | NULL |    1 | Using index                                         | 
|  3 | UNION        | Parent2    | index | NULL          | PRIMARY | 4       | NULL |    1 | Using index                                         | 
| NULL | UNION RESULT | <union2,3> | ALL   | NULL          | NULL    | NULL    | NULL | NULL |                                                     | 
+----+--------------+------------+-------+---------------+---------+---------+------+------+-----------------------------------------------------+
4 rows in set (0.00 sec)

que es razonable dado el diseño.

Ahora el problema: la consulta anterior es algo inútil, ya que no devuelve ninguna columna de los elementos principales. En el momento en que agrego más columnas a la consulta interna, ya no se utilizará ningún índice:

mysql> explain select * from Children1 c  inner join ( select id as parentId,name from Parent union  select id as parentId,name from Parent2 ) p on p.parentId = c.parentId;
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------------------------------------------+
| id | select_type  | table      | type | possible_keys | key  | key_len | ref  | rows | Extra                                               |
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------------------------------------------+
|  1 | PRIMARY      | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL | Impossible WHERE noticed after reading const tables | 
|  2 | DERIVED      | Parent     | ALL  | NULL          | NULL | NULL    | NULL |    1 |                                                     | 
|  3 | UNION        | Parent2    | ALL  | NULL          | NULL | NULL    | NULL |    1 |                                                     | 
| NULL | UNION RESULT | <union2,3> | ALL  | NULL          | NULL | NULL    | NULL | NULL |                                                     | 
+----+--------------+------------+------+---------------+------+---------+------+------+-----------------------------------------------------+
4 rows in set (0.00 sec)

¿Alguien puede explicar por qué ya no se utilizan los índices (PRINCIPALES)? ¿Existe una solución para este problema si es posible sin tener que cambiar el diseño de la base de datos?

¡Gracias!

¿Fue útil?

Solución

Creo que el optimizador se cae una vez que empiezas a extraer varias columnas en la consulta derivada debido a la posibilidad de que necesitaría convertir tipos de datos en la unión (no en este caso, pero en general). También puede deberse al hecho de que su consulta esencialmente quiere ser una subconsulta derivada correlacionada, lo que no es posible (desde dev.mysql.com ):

  

Las subconsultas en la cláusula FROM no pueden ser subconsultas correlacionadas, a menos que se usen dentro de la cláusula ON de una operación JOIN.

Lo que estás tratando de hacer (pero no es válido) es:

select * from Children1 c 
inner join (
select id as parentId from Parent where Parent.id = c.parentId
union 
select id as parentId from Parent2 where Parent.id = c.parentId
) p 

Result: "Unknown column 'c.parentId' in 'where clause'.

¿Hay alguna razón por la que no prefieras dos combinaciones izquierdas e IFNULLs?

select *, IFNULL(p1.name, p2.name) AS name from Children1 c
left join Parent p1 ON p1.id = c.parentId
left join Parent2 p2 ON p2.id = c.parentId

La única diferencia entre las consultas es que en las suyas obtendrá dos filas si hay un padre en cada tabla. Si eso es lo que quieres / necesitas, esto funcionará bien y las uniones serán rápidas y siempre harán uso de los índices:

(select * from Children1 c join Parent p1 ON p1.id = c.parentId)
union
(select * from Children1 c join Parent2 p2 ON p2.id = c.parentId)

Otros consejos

Mi primer pensamiento es insertar un " significativo " Número de registros en las tablas y use la TABLA DE ANALIZAR para actualizar las estadísticas. Una tabla con 4 registros siempre será más rápida de leer utilizando un escaneo completo en lugar de ir a través del índice. Además, puede probar USE INDEX para forzar el uso del índice y ver cómo cambia el plan.

También recomendaré leer esta documentación y ver qué bits son relevantes MYSQL :: Optimizando consultas con EXPLAIN

Este artículo también puede ser útil. 7 maneras de convencer MySQL para usar el índice correcto

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top