SQL查詢中in、exists、not in、not exists的用法與區別

來源:互聯網
上載者:User

SQL查詢中in、exists、not in、not exists的用法與區別

1、in和exists

in是把外表和內表作hash(字典集合)串連,而exists是對外表作迴圈,每次迴圈再對內表進行查詢。一直以來認為exists比in效率高的說法是不準確的,如果查詢的兩個表大小相當,那麼用in和exists差別不大;如果兩個表中一個較小一個較大,則子查詢表大的用exists,子查詢表小的用in。

例如:表A(小表),表B(大表)

方式一:索引使用

1)select * from A where id in(select id from B)   -->效率低,用到了A表上id列的索引

2)select * from A where exists(select id from B where id=A.id) -->效率高,用到了B表上id列的索引

3)select * from B where id in(select id from A)   -->效率高,用到了B表上id列的索引

4)select * from B where exists(select id from A where id=B.id) -->效率低,用到了A表上id列的索引

方式二:遍曆使用

1)in()只執行一次,它查出B表中的所有id欄位並緩衝起來。然後檢查A表的id是否與B表中的id相等,如果相等則將A表的記錄加入結果集中,直到遍曆完A表的所有記錄。

它的查詢過程類似於以下代碼的執行過程:

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;

可以看出,當B表資料較大時不適合使用in(),因為它會把B表資料全部遍曆一次。

如:A表有10000條記錄,B表有1000000條記錄,那麼最多有可能遍曆10000*1000000次,效率很差。

如:A表有10000條記錄,B表有100條記錄,那麼最多有可能遍曆10000*100次,遍曆次數大大減少,效率大大提升。

結論:in()適合B表比A表資料小的情況

2)exists()會執行A.length次,它並不緩衝exists()結果集,因為exists()結果集的內容並不重要,重要的是其內查詢語句的結果集空或者非空,空則返回false,非空則返回true。

它的查詢過程類似於以下代碼的執行過程:

List resultSet={};

Array A=(select * from A);

for(int i=0;i<A.length;i++) {

if(exists(A[i].id) {  //執行select id from B where B.id=A.id是否有記錄返回

resultSet.add(A[i]);

}

}

return resultSet;

當B表比A表資料大時適合使用exists(),因為它沒有那麼多遍曆操作,只需要再執行一次查詢就行。

如: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()需要查詢資料庫,我們都知道查詢資料庫所消耗的效能更高,而記憶體比較很快。

結論:exists()適合B表比A表資料大的情況。

當A表資料與B表資料一樣大時,in與exists效率差不多,可任選一個使用。

2、not in 和not exists

not in 邏輯上不完全等同於not exists,如果你誤用了not in,小心你的程式存在致命的bug。

請看下面的例子:

create table A1 (c1 int,c2 int);

create table A2 (c1 int,c2 int);

insert into A1 values(1,2);

insert into A1 values(1,3);

insert into A2 values(1,2);

insert into A2 values(1,null);

 

select * from A1 where c2 not in(select c2 from A2);                            -->執行結果:無(null)

select * from A1 where not exists(select c2 from A2 where A2.c2=A1.c2);    -->執行結果:1  3

正如所看到的,not in出現了不期望的結果集,存在邏輯錯誤。使用not in(它會調用子查詢),而使用not exists(它會調用關聯子查詢)。如果子查詢中返回的任意一條記錄含有空值,則查詢將不返回任何記錄。如果子查詢欄位有非空限制,這時可以使用not in。

如果查詢語句使用了not in,那麼對內外表都進行全表掃描,沒有用到索引;而not exists的子查詢依然能用到表上的索引。所以無論哪個表大,用not exists都比not in 要快。

3、in 和 =

select name from employee where name in('張三','李四','王五');

select name from employee where name='張三' or name='李四' or name='王五';

的結果是相同的。

4.exists防止插入重複記錄

有時需要插入非重複記錄,在Mysql中可以使用ignore關鍵字來忽略已有記錄,但是其只能通過主鍵忽略,不能根據自訂條件忽略。

其文法為:insert ignore into tableName (column1,column2,……) values (value1,value2,……);

但是其他資料庫不一定提供類似ignore關鍵字,所以可以使用exists條件句防止插入重複記錄。

insert into A (name,age) select name,age from B where not exists (select 1 from A where A.id=B.id);

5.關於exists更多說明

exists用於檢查子查詢返回的結果集是否為空白,該子查詢實際上並不返回任何資料,而是傳回值true或false。
文法: exists subQuery

參數: subQuery 是一個受限的 select 語句 (不允許有 compute 子句和 into 關鍵字)。

結果類型: boolean 如果子查詢包含行,則返回 true ,否則返回 false 。
結論:select * from A where exists (select 1 from B where A.id=B.id);

一種通俗的可以理解為:將外查詢表的每一行,代入內查詢作為檢驗,如果內查詢返回的結果集非空,則exists子句返回true,這一行方可作為外查詢的結果行,否則不能作為結果。

--------以上sql內容根據網上提供的資料整理出的結果,均適用與Mysql、Sql Server、Oracle。

本文永久更新連結地址:

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.