1

MySQL 5.0.75-0ubuntu10.2我有一个像这样的固定表格布局:

parent带 id 的表带parent2id 的表children1带 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

一个孩子在其中一个表Parent或中有一个父母Parent2。当我需要生孩子时,我会使用这样的查询:

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

解释这个查询产生:

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

考虑到布局,这是合理的。

现在的问题是:前面的查询有点没用,因为它没有从父元素返回任何列。在我向内部查询添加更多列的那一刻,将不再使用索引:

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)

谁能解释为什么不再使用(PRIMARY)索引?如果可能,是否有解决此问题的方法而无需更改数据库布局?

谢谢!

4

2 回答 2

1

我认为,一旦您开始在派生查询中提取多个列,优化器就会崩溃,因为它可能需要转换联合上的数据类型(不是在这种情况下,而是在一般情况下)。这也可能是由于您的查询本质上想要成为相关的派生子查询,这是不可能的(来自dev.mysql.com):

FROM 子句中的子查询不能是相关子查询,除非在 JOIN 操作的 ON 子句中使用。

您正在尝试做的(但无效)是:

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

您是否有理由不喜欢两个左连接和 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

查询之间的唯一区别是,如果每个表中都有父级,则在您的查询中您将获得两行。如果这是您想要/需要的,那么这也可以很好地工作,并且连接将很快并且始终使用索引:

(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)
于 2009-12-16T09:33:42.340 回答
0

我的第一个想法是在表中插入“大量”记录并使用 ANALYZE TABLE 来更新统计信息。使用完整扫描而不是通过索引读取具有 4 条记录的表总是更快!此外,您可以尝试 USE INDEX 来强制使用索引并查看计划如何更改。

我还将推荐阅读此文档并查看哪些位是相关的 MYSQL::Optimizing Queries with EXPLAIN

这篇文章也很有用 7 种方法来说服 MySQL 使用正确的索引

于 2009-10-22T08:59:29.513 回答