簡體   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