我有两个表:(省略与此问题无关的列):
CREATE TABLE 'oc_room' (
'id' int(11) NOT NULL AUTO_INCREMENT,
'house_id' int(11) NOT NULL,
'style_id' int(11) DEFAULT NULL,
'weight' int(11) DEFAULT '0',
'state' tinyint(4) DEFAULT '0',
-- (more columns, omitted for clarity)
PRIMARY KEY ('id'),
KEY 'house_id' ('house_id'),
KEY 'style_id' ('style_id'),
KEY 'butler_id' ('butler_id'),
KEY 'oc_room_house_state_hidden_ik_1' ('house_id','state','hidden'),
CONSTRAINT 'oc_room_ibfk_1' FOREIGN KEY ('house_id') REFERENCES 'oc_house' ('id'),
CONSTRAINT 'oc_room_ibfk_2' FOREIGN KEY ('style_id') REFERENCES 'oc_room_style' ('id'),
CONSTRAINT 'oc_room_ibfk_3' FOREIGN KEY ('butler_id') REFERENCES 'oc_butler' ('id')
) ENGINE=InnoDB AUTO_INCREMENT=267 DEFAULT CHARSET=utf8;
CREATE TABLE 'oc_circle_of_community' (
'id' int(11) NOT NULL AUTO_INCREMENT,
'circle_id' int(11) NOT NULL,
'community_id' int(11) NOT NULL,
PRIMARY KEY ('id'),
KEY 'circle_id' ('circle_id'),
KEY 'community_id' ('community_id'),
CONSTRAINT 'oc_circle_of_community_ibfk_1' FOREIGN KEY ('circle_id') REFERENCES 'oc_circle' ('id'),
CONSTRAINT 'oc_circle_of_community_ibfk_2' FOREIGN KEY ('community_id') REFERENCES 'oc_community' ('id')
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;
当我explain
选择陈述时,我有两个问题
让我们从两张图片开始:
然后将PIC-1中的表与PIC-2中的表进行比较。你会发现:
oc_room
的选择使用组合键oc_room_house_state_hidden_ik_1
。oc_room.id in (5,7,9,20,40,60 )
已取代oc_room.id in
( select id
from oc_house
where community_id in
( select community_id
from oc_circle_of_community
where circle_id in
( select id
from oc_circle
where oc_circle.district_id in
( select id
from oc_district
where oc_district.id = 3 ))))
为什么会有差异?
表oc_room
中共有约300行。
查看表格的第2行PIC-2,其中解释了表格oc_circle_of_community
的选择。有两种可能的键:circle_id
和community_id
。为什么不使用两把钥匙?
(表oc_circle_of_community
中共有14行。这可能会有所帮助。)
答案 0 :(得分:1)
来自bottom的手册。
索引对于小型表或大型表的查询不太重要 报表查询处理大多数或所有行的位置。当一个查询 需要访问大多数行,顺序读取比快 通过索引工作。顺序读取可以最大限度地减少磁盘搜索 如果不是查询所需的所有行。
a)300行的数量太小,无法在单个索引上进行磨练,然后进行扫描,以便根据索引中断,或者
b)您尝试使用足够的复合索引,或
c)你用covering index寻找黄金并避免数据页读取
但注意,它解决了8行,而不是300行。它采用了复合(house_id
,state
,hidden
),其中最后一行没有显示你。总共7个字节。
由于您只有300行,analyze table
应该只需要一秒钟。它刷新了关键分布的统计数据,而不是变得陈旧,从而迫使密钥不被使用。密钥可能是使用目标,但最终在执行期间被放弃。因此,它是关于其有用性的一般声明,例如对于大表,而不是您的问题。
REF为NULL的14行与本答案的开头有关。
答案 1 :(得分:0)
第一个查询使用索引,因为它是"覆盖"。也就是说,SELECT
中的所有字段都在该索引中:
好吧,我错了。它说Using index condition
,指的是Index Condition Pushdown
;它没有说Using index
,这意味着"覆盖"。 ORDER BY weight...
阻止"覆盖"。要获得更多信息,请EXPLAIN FORMAT=JSON SELECT ...
。
对于第二个查询,请勿使用IN ( SELECT ... )
,它会非常优化非常。相反,变成JOIN
。完成后,如果仍有必要,我们可以讨论它的性能。
我们可能会发现它仍然没有使用索引,但是运行得足够快,不用担心。
复合索引应该启动,其中任何列都是" =常量" (hidden
,在第一个查询中)。优化器不会处理多个"范围"同时,可能处理IN ( constants )
。我Index Cookbook中的更多讨论。
(无关...)
难道state
不能为空吗?