繁体   English   中英

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

[英]MySQL: Why Select .. IN with subquery could not use index

我开始学习 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 index NULL time 15 NULL 3 50.00 Using where; 使用索引

我的猜测是因为 order by 具有更高的优先级并且它是最左边的索引,所以我们首先使用它。 然后通过将海报添加到复合索引中,我们仍然可以使用这个复合索引做过滤,最后返回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 表有使用临时; 使用文件排序。 我也试过 left join 它也有相同的解释输出

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

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

如何优化我的索引和查询?

谢谢

不是不能,是没有好处。

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

在您的情况下,扫描表格会更快。 另一种方法是使用索引来隔离需要底层表中的哪些行然后转到底层表以获取这些行。

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

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


或者,使用覆盖索引。 一个包含您需要搜索的所有列以及您将包含在 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)

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

第二个需要扫描整个表或查找索引并连接到表。 因为表很小,优化器将决定只扫描整个表,因为这样会更便宜。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM