2009-10-22 8 views
1

En MySQL 5.0.75-0ubuntu10.2 j'ai une disposition de table fixe comme ça:MySQL: Utilisation des indices UNION subselects

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. Quand je dois obtenir un enfant que j'utilise une requête comme ça:

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 

Expliquer ce rendement de la requête:

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

qui est raisonnable compte tenu de la mise en page.

Maintenant le problème: La requête précédente est un peu inutile, car il renvoie aucune colonne des éléments parents. En ce moment, j'ajouter plusieurs colonnes à la requête intérieure aucun index sera utilisé plus:

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 (primaire) ne sont pas utilisés plus? Y a-t-il une solution de contournement pour ce problème si possible sans avoir à changer la disposition DB?

Merci!

Répondre

1

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

Sous-requêtes dans la clause FROM ne peut pas être sous-requêtes corrélées, à moins utilisé dans le 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 at-il une raison que vous ne préférez pas deux à gauche et rejoint 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 seule différence entre les requêtes est que dans le vôtre, vous obtiendrez deux lignes s'il y a un parent dans chaque table.Si c'est ce que vous voulez/besoin alors cela va bien travailler aussi et se joint sera rapide et toujours utiliser des indices:

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

Vous avez probablement raison. Pourquoi j'ai posé la question: j'ai un modèle Hibernate qui produit ces requêtes mais beaucoup plus compliquées qu'ici. J'ai réfléchi à la façon d'améliorer la génération de requêtes de Hibernate pour le mappage de l'héritage de la sous-classe union à la lumière des capacités de MySQL. Mais depuis Hibernate et MySQL ont leurs lacunes dans cette direction, j'ai depuis abandonné l'effort et juste réarrangé la structure de la base de données. Merci de prendre le temps. – jrudolph

0

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

Je vais aussi recommande la lecture de cette documentation et voir quels bits sont pertinents MYSQL::Optimizing Queries with EXPLAIN

Cet article peut aussi être utile 7 ways to convince MySQL to use the right index

+0

Cela ne veut pas répondre à la question particulière, mais de toute façon thx. – jrudolph