MySQL:为什么选择带有子查询的IN不能使用索引

时间:2018-11-06 16:20:28

标签: mysql sql database indexing

我开始学习MySQL,并遇到一些有关为子查询或联接建立索引的问题。我有两个创建如下的表

create table User(id integer, poster integer, PRIMARY KEY (id,poster));
insert into User(id, poster) values(1, 123);
insert into User(id, poster) values(1, 345);
insert into User(id, poster) values(2, 123);


create table Feed(id integer, poster integer, c integer, time integer, PRIMARY KEY(id),  INDEX(poster),INDEX(time,c));
insert into Feed(id, poster, c,time) values(1, 123, 0, 2);
insert into Feed(id, poster, c,time) values(2, 123,1,1);
insert into Feed(id, poster, c,time) values(3, 345,2,3);

我最初尝试了一些简单的查询,例如

1. Select poster from User where id =1;  
2. Select c from Feed where poster = 1;
3. Select c from Feed where poster in (1,2,3) 

第三个查询的解释类似于

SIMPLE  Feed    NULL    ALL poster  NULL    NULL    NULL    3   100.00  Using where; Using filesort

我不确定为什么需要文件排序。但是在Feed表中添加了复合索引INDEX(time,poster,c)后,相同的查询将使用索引

这是新的创建表查询

   create table Feed(id integer, poster integer, c integer, time integer, PRIMARY KEY(id),INDEX(time,poster, c));

这里是带有新的复合索引的说明输出     1 SIMPLE Feed NULL索引NULL时间15 NULL 3 50.00使用where;使用索引

我的猜测是,由于order by具有更高的优先级,并且它是最左边的索引,因此我们首先使用它。然后通过将poster添加到复合索引中,我们仍然可以使用此复合索引进行过滤,最后返回c。

然后我尝试了一些子查询

explain SELECT Feed.c from Feed where Feed.poster IN(select poster from User where id =1) order by Feed.time; 

这里没什么好看的,我只是用子查询替换了硬编码的(1,2,3)。我希望看到相同的解释结果,但是我得到

1   SIMPLE  User    NULL    ref PRIMARY,poster  PRIMARY 4   const   1   100.00  Using index; Using temporary; Using filesort
1   SIMPLE  Feed    NULL    index   NULL    time    15  NULL    3   33.33   Using where; Using index; Using join buffer (Block Nested Loop)

我很好奇,为什么USER表具有“使用临时”;使用文件排序。我也尝试过左连接,它也有相同的说明输出

explain SELECT Feed.c
FROM `Feed` 
LEFT JOIN `User` on User.poster = Feed.poster where User.id = 1 order by Feed.time;

根据我的阅读,我们应该避免使用文件排序和temporaray文件。

如何优化索引和查询?

谢谢

1 个答案:

答案 0 :(得分:1)

不是 不能 ,而是没有好处。

索引有点像另一个可以首先连接的表,以帮助连接到实际表。

对于您而言,扫描表的速度更快。另一种选择是使用索引来隔离基础表中哪些行是必需的 ,然后转到基础表以获取那些行。

如果您的表长了一百万行,那将有所不同。那么值得使用索引来减少扫描表的工作。

因此,编写一个可以创建更多随机数据的测试床,然后您就可以看到它。


或者,使用覆盖索引。其中一个包含您需要搜索的所有列以及要包含在SELECT和JOIN中的所有列。

在下面的示例中,我将(用于表格Feed) INDEX(poster)更改为INDEX(poster, c)。现在,如果查询计划者从索引中读取数据,它也立即知道c的值,而无需“联接”到基础表。

create table User(id integer, poster integer, PRIMARY KEY (id,poster), INDEX(poster));
insert into User(id, poster) values(1, 123);
insert into User(id, poster) values(1, 345);
insert into User(id, poster) values(2, 123);

create table Feed(id integer, poster integer, c integer, time integer, PRIMARY KEY(id),  INDEX(poster, c),INDEX(time,c));
insert into Feed(id, poster, c,time) values(1, 123, 0, 2);
insert into Feed(id, poster, c,time) values(2, 123,1,1);
insert into Feed(id, poster, c,time) values(3, 345,2,3);

现在,比较两个查询...

Select c from Feed where poster in (1,2,3)

SELECT c, time FROM feed WHERE poster IN (1,2,3)

第一个可以通过索引来回答。

第二个需要扫描整个表或在索引上查找并联接到表。由于表格太小,因此优化程序将决定只扫描整个表格,因为这样会更便宜。