MySQL:UNION 子选择中索引的使用
Posted
技术标签:
【中文标题】MySQL:UNION 子选择中索引的使用【英文标题】:MySQL: Usage of indices in UNION subselects 【发布时间】:2009-10-22 07:51:30 【问题描述】:在mysql 5.0.75-0ubuntu10.2
我有一个这样的固定表格布局:
带有 id 的表 parent
带有 id 的表 parent2
带有 parentId 的表 children1
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) 索引?如果可能,是否有解决此问题的方法而无需更改数据库布局?
谢谢!
【问题讨论】:
【参考方案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)
【讨论】:
你可能是对的。为什么我问这个问题:我有一个 Hibernate 模型,它产生这些查询,但比这里复杂得多。结合 MySQL 的能力,思考了如何改进 Hibernate 对联合子类继承映射的查询生成。但是由于 Hibernate 和 MySQL 在这个方向上存在不足,所以我后来放弃了努力,只是重新安排了数据库结构。感谢您抽出宝贵时间。【参考方案2】:我的第一个想法是在表中插入“大量”记录并使用 ANALYZE TABLE 更新统计信息。使用完整扫描而不是通过索引读取具有 4 条记录的表总是更快! 此外,您可以尝试使用 USE INDEX 来强制使用索引并查看计划如何变化。
我还建议阅读此文档并查看哪些内容是相关的 MYSQL::Optimizing Queries with EXPLAIN
这篇文章也很有用 7 ways to convince MySQL to use the right index
【讨论】:
以上是关于MySQL:UNION 子选择中索引的使用的主要内容,如果未能解决你的问题,请参考以下文章