1. 程式人生 > 其它 >SQL優化——IN和EXISTS誰的效率更高

SQL優化——IN和EXISTS誰的效率更高

IN和EXISTS被頻繁使用在SQL中,雖然作用是一樣的,但是在使用效率誰更高這點上眾說紛紜。下面我們就通過一組測試來看,在不同場景下,使用哪個效率更高。

測試資料:

B表: 大表,大約300000行資料

CREATE TABLE `B` (
`id` int NOT NULL AUTO_INCREMENT,
`B_id` int NOT NULL,
`value` varchar(20) NOT NULL,
`flag` int not null,
PRIMARY KEY (`id`),
KEY `idx_b_flag` (`flag`),
KEY `idx_b_id` (`B_id`)
)


A表: 小表,20000行資料

CREATE TABLE `A` (
`id` int NOT NULL AUTO_INCREMENT,
`flag` int NOT NULL,
`value` varchar(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_a_flag` (`flag`)
)

測試1:

子查詢 select flag from B where B_id<100 結果集99條。

select * from A where flag in (select flag from B where B_id<100 );198 rows in set (0.00 sec)select * from A where exists (select * from B where B_id<100 and A.flag=B.flag);198 rows in set (0.10 sec)

可以看到本次測試IN效率高於EXISTS。

再看執行計劃:

IN的執行計劃:

(1)執行A表的查詢,查詢條件是A.flag在結果集B裡面,可以使用到A表的索引flag;

(2)執行B表的子查詢,得到結果集B,可以使用到B表的索引B_id。

EXISTS的執行計劃:

(1)先將A表所有記錄取到;

(2)逐行鍼對A表的記錄,去關聯B表,判斷B表的子查詢是否有返回資料,5.5之後的版本使用Block Nested Loop(Block 巢狀迴圈);

(3)如果子查詢有返回資料,則將A當前記錄返回到結果集。

A相當於取全表資料遍歷,B可以使用到索引。

測試2

子查詢 select flag from B where B_id>100 結果集 299899條。

select * from A where flag in (select flag from B where B_id>100 );
19798 rows in set (0.09 sec)select * from A where exists (select * from B where B_id>100 and A.flag=B.flag);19798 rows in set (0.06 sec)

可以看到本次EXISTS效率比IN高。

再看執行計劃:

兩者的索引使用情況與第一次實驗是一致的,當子查詢結果集很大,而外部表較小的時候,Exists的Block Nested Loop(Block 巢狀迴圈)的作用開始顯現,查詢效率會優於IN。

從兩次測試來看,並不能說明誰的效率更高,而應該具體情況具體分析:

首先來看IN和EXISTS的執行原理:

IN是做外表和內表通過Hash連線,先查詢子表,再查詢主表,不管子查詢是否有資料,都對子查詢進行全部匹配。

EXISTS是外表做loop迴圈,先主查詢,再子查詢,然後去子查詢中匹配,如果匹配到就退出子查詢返回true,將結果放到結果集。

IN原理

在in()的執行中,先執行內表得到結果集,再執行外表。外表會對所有的內表結果集匹配,也就是說:如果外表有100,內表有10000,就會執行100*10000次。所以在內表比較大的時候,不合適用in()方法,效率比較低。

select * from 外表 a where id in (select 相關id from 內表) IN的執行類似如下:

List resultSet=[];
Array A=(select * from A);
Array B=(select id from B);

for(int i=0;i<A.length;i++) {
for(int j=0;j<B.length;j++) {
if(A[i].id==B[j].id) {
resultSet.add(A[i]);
break;
}
}
}
return resultSet;

EXISTS原理

exists()的執行過程中,並沒有對每一條內表的資料都進行查詢,而是存在該條資料的時候會將結果集存起來,到最後的時候同一輸出結果集。

select a.* from 外表 a where exists(select 1 from 內表 b where a.id=b.id) 的EXISTS的執行語句如下:

List resultSet=[];
Array A=(select * from 外表 A)

for(int i=0;i<A.length;i++) {
if(exists(A[i].id) { //執行select 1 from 內表 b where b.id=a.id是否有記錄返回
resultSet.add(A[i]);
}
}
return resultSet;

設:外表A,內表B。

A表有10000條記錄,B表有1000000條記錄, 那麼exists()會執行10000次去判斷A表中的id是否與B表中的id相等。

A表有10000條記錄,B表有100000000條記錄,那麼exists()還是執行10000次,因為它只執行A.length次,可見B表資料越多,越適合exists()發揮效果。

再如:A表有10000條記錄,B表有100條記錄,那麼exists()還是執行10000次,還不如使用in()遍歷10000*100次,因為in()是在記憶體裡遍歷比較,而exists()需要查詢資料庫,我們都知道查詢資料庫所消耗的效能更高,而記憶體比較很快。

# 總結 #

1、IN查詢在內部表和外部表上都可以使用到索引;

2、EXISTS查詢僅內部表上可以使用到索引,外表會全表掃描;當子查詢結果集很大,而外部表較小的時候,EXISTS的Block Nested Loop(Block 巢狀迴圈)的作用開始顯現,查詢效率會優於IN;

3、當子查詢結果集較小,而外部表很大的時候,EXISTS的Block巢狀迴圈優化效果不明顯,IN 的外表索引優勢佔主要作用,此時IN的查詢效率會優於EXISTS。

子查詢結果集越大用EXISTS,子查詢結果集越小用IN。

轉載自資料和雲公眾號

螃蟹在剝我的殼,筆記本在寫我,漫天的我落在楓葉上雪花上,而你在想我。 --章懷柔