Pergunta

Em MySQL 5.0.75-0ubuntu10.2 Eu tenho um layout de tabela fixo assim:

parent Tabela com um id parent2 mesa com um id children1 mesa com um 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

A criança tem um pai em um dos Parent tabelas ou Parent2. Quando eu preciso para obter uma criança eu uso uma consulta assim:

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 este rendimentos de 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 é razoável dado o layout.

Agora o problema: a consulta anterior é um pouco inútil, uma vez que retorna sem colunas dos elementos pai. No momento em que eu adicionar mais colunas para a consulta interna será utilizado nenhum índice mais:

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)

Alguém pode explicar por que os índices (primário) não são usados ??mais? Existe uma solução para este problema, se possível, sem ter que mudar o layout DB?

Obrigado!

Foi útil?

Solução

Eu acho que o otimizador cai uma vez que você começar a puxar para fora várias colunas na consulta derivados por causa da possibilidade que seria necessário para converter tipos de dados sobre a união (não neste caso, mas em geral). Ele também pode ser devido ao fato de que sua consulta essencialmente quer ser uma subconsulta derivado correlacionados, o que não é possível (a partir de dev.mysql.com ):

Subqueries na cláusula FROM não pode ser subconsultas correlacionadas, a não ser utilizados na cláusula ON de um JOIN operação.

O que você está tentando fazer (mas não é válido) é:

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

Existe uma razão você não prefere dois esquerda junções 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

A única diferença entre as consultas é que, em seu você vai ter duas linhas se houver uma mãe em cada mesa. Se é isso que você quer / precisa, então isso vai funcionar bem também e se junta vai ser rápido e sempre fazer uso dos í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)

Outras dicas

Meu primeiro pensamento é inserir um número "significativo" de registros nas tabelas e uso ANALYZE TABLE para atualizar as estatísticas. Uma mesa com 4 registros será sempre mais rápido para ler usando uma varredura completa em vez de ir através do índice! Além disso, você pode tentar USE INDEX para forçar o uso do índice e olhar como as mudanças de plano.

Além disso, vou recomendar a leitura desta documentação e veja quais bits são relevantes MYSQL :: otimização de consultas com EXPLICAR

Este artigo também pode ser útil 7 maneiras de convencer MySQL para usar o indicador direito

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top