Question

Dans MySQL 5.0.75-0ubuntu10.2 , la disposition des tableaux est la suivante:

Table parent avec un identifiant Table parent2 avec un identifiant Table children1 avec un parentId

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 enfant a un parent dans l’une des tables Parent ou Parent2 . J'utilise une requête comme celle-ci lorsque j'ai besoin d'avoir des enfants:

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

En expliquant cette requête donne:

+----+--------------+------------+-------+---------------+---------+---------+------+------+-----------------------------------------------------+
| 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)

ce qui est raisonnable compte tenu de la présentation.

Maintenant le problème: la requête précédente est quelque peu inutile, car elle ne renvoie aucune colonne des éléments parents. Au moment où j'ajoute plus de colonnes à la requête interne, aucun index ne sera utilisé:

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)

Quelqu'un peut-il expliquer pourquoi les indices (PRIMARY) ne sont plus utilisés? Existe-t-il une solution de contournement à ce problème si possible sans avoir à modifier la présentation de la base de données?

Merci!

Était-ce utile?

La solution

Je pense que l'optimiseur tombe en panne une fois que vous avez commencé à extraire plusieurs colonnes de la requête dérivée en raison de la possibilité qu'il aurait besoin de convertir des types de données sur l'union (pas dans ce cas, mais en général). Cela peut également être dû au fait que votre requête veut essentiellement être une sous-requête dérivée corrélée, ce qui n’est pas possible (from dev.mysql.com ):

  

Les sous-requêtes de la clause FROM ne peuvent pas être corrélées, sauf si elles sont utilisées dans la clause ON d'une opération JOIN.

Ce que vous essayez de faire (mais n’est pas valide) est:

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'.

Y a-t-il une raison pour laquelle vous ne préférez pas deux jointures à gauche et IFNULL:

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 seule différence entre les requêtes est que dans la vôtre, vous obtenez deux lignes s'il y a un parent dans chaque table. Si c'est ce que vous voulez / avez besoin, cela fonctionnera également et les jointures seront rapides et utiliseront toujours les index:

(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)

Autres conseils

Ma première pensée est d’insérer un "significatif". nombre d'enregistrements dans les tables et utilisez ANALYZE TABLE pour mettre à jour les statistiques. Une table avec 4 enregistrements sera toujours plus rapide à lire en utilisant une analyse complète plutôt que de passer par l'index! De plus, vous pouvez essayer USE INDEX pour forcer l’utilisation de l’index et voir comment le plan change.

Je vous recommande également de lire cette documentation et de voir quels bits sont pertinents. MYSQL :: Optimisation des requêtes avec EXPLAIN

Cet article peut aussi être utile 7 façons de convaincre MySQL pour utiliser le bon index

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top