Mysql 中 exists 和 in 的區別

最近刷到面試題:Mysql 中 exists 和 in 的區別,先說下答案。

 

下面將主查詢的表稱爲外表;子查詢的表稱爲內表。exists 與 in 的區別如下:

  • 子查詢使用 exists,會先進行主查詢,將查詢到的每行數據循環帶入子查詢校驗是否存在,過濾出整體的返回數據;子查詢使用 in,會先進行子查詢獲取結果集,然後主查詢匹配子查詢的結果集,返回數據
  • 外表內表相對大小情況不一樣時,查詢效率不一樣:兩表大小相當,in 和 exists 差別不大;內表大,用 exists 效率較高;內表小,用 in 效率較高。
  • 不管外表與內表的大小,not exists 的效率一般要高於 not in,跟子查詢的索引訪問類型有關。

 

建表、造數據,驗證一下以上答案。

# 建表 student1
drop table if exists student1;
create table student1(
sid int primary key auto_increment,
sname varchar(40)
);

# 建存儲過程給表 student1,插入1000條數據
drop procedure if exists addStudent1;
create procedure addStudent1()
BEGIN
	declare idx int;
	set idx = 1;
	while idx <= 1000 DO
	insert into student1 values(null, concat('student-', idx));
	set idx = idx + 1;
  end while;
end;

call addStudent1();

select * from student1;

# 建表 student2
drop table if exists student2;
create table student2(
sid int primary key auto_increment,
sname varchar(40)
);

# 建存儲過程給表 student2,插入100000條數據
drop procedure if exists addStudent2;
create procedure addStudent2()
BEGIN
	declare idx int;
	set idx = 1;
	while idx <= 100000 DO
	insert into student2 values(null, concat('student-', idx));
	set idx = idx + 1;
  end while;
end;

call addStudent2();

select * from student2;

 

in 與 exists 的查詢 SQL

select count(1) from student1 where sname in (select sname from student2);
select count(1) from student1 where exists (select sname from student2 where student2.sname = student1.sname);
select count(1) from student2 where sname in (select sname from student1);
select count(1) from student2 where exists (select sname from student1 where student2.sname = student1.sname);

執行時間:

[SQL] select count(1) from student1 where sname in (select sname from student2);
受影響的行: 0
時間: 0.092s

[SQL] 
select count(1) from student1 where exists (select sname from student2 where student2.sname = student1.sname);
受影響的行: 0
時間: 0.076s

[SQL] 
select count(1) from student2 where sname in (select sname from student1);
受影響的行: 0
時間: 14.820s

[SQL] 
select count(1) from student2 where exists (select sname from student1 where student2.sname = student1.sname);
受影響的行: 0
時間: 15.144s

結論:student2 大表在內適用 exists,所以第 2 條 SQL 比第 1 條快;student1 小表在內適用 in,所以第 3 條 SQL 比第 4 條快。

 

not in 與 not exists 的查詢 SQL

select count(1) from student1 where sname not in (select sname from student2);
select count(1) from student1 where not exists (select sname from student2 where student2.sname = student1.sname);
select count(1) from student2 where sname not in (select sname from student1);
select count(1) from student2 where not exists (select sname from student1 where student2.sname = student1.sname);

執行時間:

[SQL] 
select count(1) from student1 where sname not in (select sname from student2);
受影響的行: 0
時間: 0.079s

[SQL] 
select count(1) from student1 where not exists (select sname from student2 where student2.sname = student1.sname);
受影響的行: 0
時間: 0.075s

[SQL] select count(1) from student2 where sname not in (select sname from student1);
受影響的行: 0
時間: 15.797s

[SQL] 
select count(1) from student2 where not exists (select sname from student1 where student2.sname = student1.sname);
受影響的行: 0
時間: 15.160s

結論:not exists 性能高於 not in

 

給 student1、student2 sname 字段,加上索引,上述結論仍然成立。

create index idx_1 on student1(sname);
create index idx_2 on student2(sname);

執行時間:

[SQL] select count(1) from student1 where sname in (select sname from student2);
受影響的行: 0
時間: 0.022s

[SQL] 
select count(1) from student1 where exists (select sname from student2 where student2.sname = student1.sname);
受影響的行: 0
時間: 0.014s

[SQL] 
select count(1) from student2 where sname in (select sname from student1);
受影響的行: 0
時間: 0.379s

[SQL] 
select count(1) from student2 where exists (select sname from student1 where student2.sname = student1.sname);
受影響的行: 0
時間: 0.373s

[SQL] 
select count(1) from student1 where sname not in (select sname from student2);
受影響的行: 0
時間: 0.006s

[SQL] 
select count(1) from student1 where not exists (select sname from student2 where student2.sname = student1.sname);
受影響的行: 0
時間: 0.006s

[SQL] 
select count(1) from student2 where sname not in (select sname from student1);
受影響的行: 0
時間: 0.455s

[SQL] 
select count(1) from student2 where not exists (select sname from student1 where student2.sname = student1.sname);
受影響的行: 0
時間: 0.418s

 

再細看一下,not in 與 not exists 查詢索引使用情況

not in,子查詢使用了 index_subquery 訪問類型

EXPLAIN EXTENDED select count(1) from student2 where sname not in (select sname from student1);
SHOW WARNINGS;

 

not exists,子查詢使用了 ref 訪問類型

EXPLAIN EXTENDED select count(1) from student2 where not exists (select sname from student1 where student2.sname = student1.sname);
SHOW WARNINGS;

 

 


【Java面試題與答案】整理推薦

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章