MySQL大师:为什么2个查询给出不同的“解释”索引使用结果?

时间:2011-04-23 22:52:07

标签: mysql sql optimization

此查询:

explain 
SELECT `Lineitem`.`id`, `Donation`.`id`, `Donation`.`order_line_id` 
  FROM `order_line` AS `Lineitem` 
       LEFT JOIN `donations` AS `Donation` 
       ON (`Donation`.`order_line_id` = `Lineitem`.`id`) 
 WHERE `Lineitem`.`session_id` = '1'

正确使用{EXPLES输出中显示的Donation.order_line_idLineitem.id索引:

id  select_type     table       type    possible_keys   key             key_len     ref         rows    Extra
1   SIMPLE          Lineitem    ref     session_id      session_id      97          const       1       Using where; Using index
1   SIMPLE          Donation    ref     order_line_id   order_line_id   4           Lineitem.id 2       Using index

但是,这个查询只包含另一个字段:

explain 
SELECT `Lineitem`.`id`, `Donation`.`id`, `Donation`.`npo_id`, 
       `Donation`.`order_line_id` 
  FROM `order_line` AS `Lineitem`  
       LEFT JOIN `donations` AS `Donation` 
       ON (`Donation`.`order_line_id` = `Lineitem`.`id`) 
 WHERE `Lineitem`.`session_id` = '1'

显示Donation表不使用索引:

id  select_type     table       type    possible_keys   key             key_len     ref     rows    Extra
1   SIMPLE          Lineitem    ref     session_id      session_id      97          const   1       Using where; Using index
1   SIMPLE          Donation    ALL     order_line_id   NULL            NULL        NULL    3

表中的所有_id字段都已编制索引,但我无法弄清楚如何将此字段添加到所选字段列表中会导致索引被删除。

根据James C的要求,以下是表格定义:

CREATE TABLE `donations` (
`id` int(10) unsigned NOT NULL auto_increment,
`npo_id` int(10) unsigned NOT NULL,
`order_line_detail_id` int(10) unsigned NOT NULL default '0',
`order_line_id` int(10) unsigned NOT NULL default '0',
`created` datetime default NULL,
`modified` datetime default NULL,
PRIMARY KEY  (`id`),
KEY `npo_id` (`npo_id`),
KEY `order_line_id` (`order_line_id`),
KEY `order_line_detail_id` (`order_line_detail_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8

CREATE TABLE `order_line` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`order_id` bigint(20) NOT NULL,
`npo_id` bigint(20) NOT NULL default '0',
`session_id` varchar(32) collate utf8_unicode_ci default NULL,
`created` datetime default NULL,
PRIMARY KEY  (`id`),
KEY `order_id` (`order_id`),
KEY `npo_id` (`npo_id`),
KEY `session_id` (`session_id`)
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8

我也做了一些关于基数的阅读,看起来Donations.npo_idDonations.order_line_id的基数都是2.希望这表明有用的东西?

我认为USE INDEX可能会解决问题,但我正在使用ORM,这使得这有点棘手,我不明白为什么它不能抓住正确的索引JOIN专门命名索引字段?!?

感谢您的智慧!

1 个答案:

答案 0 :(得分:3)

第一个解释最后有“使用索引”。这意味着它能够找到行通过查看索引而不必获取/分析任何行数据来返回查询结果。

在第二个查询中添加一个可能未编入索引的行。这意味着MySQL必须查看表的数据。我不确定为什么优化器选择进行表扫描,但我认为如果表格相当小,它可能更容易阅读所有内容而不是尝试挑选各行的详细信息。

编辑:我认为添加以下索引会进一步改善,并且只允许所有连接使用索引:

ALTER TABLE order_line ADD INDEX(session_id, id);
ALTER TABLE donations ADD INDEX(order_line_id, npo_id, id)

这将允许order_line使用session_id查找行,然后返回id并允许donations加入order_line_id,然后返回另外两列。

查看auto_increment值,我可以假设其中没有太多数据。值得注意的是,表格中的数据量会对查询计划产生影响,最好将一些样本数据放在那里进行测试。有关详细信息,请查看此博客文章,我花了一些时间回顾:http://webmonkeyuk.wordpress.com/2010/09/27/what-makes-a-good-mysql-index-part-2-cardinality/