之前一直在网上看到说数据in查询的效率不高,不会使用索引,要用exists替代。近期的项目采用分布式开发,经常会id上使用in查询,发现效率蛮高的,很明显使用了索引,这里整理下。
如下两张测试表,emp和dept。其中emp的deptno有索引为num1,和dept的deptno相关联,dept的deptno为主键。其中emp大概有500W条数据,dept有100条数据。
采用mysql测试
mysql> explain select * from emp where emp.deptno = '339558' ;
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+
| 1 | SIMPLE | emp | ref | num1 | num1 | 3 | const | 57 | |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+
mysql> explain select * from emp where emp.deptno in('339558');
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+
| 1 | SIMPLE | emp | ref | num1 | num1 | 3 | const | 57 | |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+
结果显示使用in和使用=与的执行计划是一样的。因此in的效率是有保障的。
mysql> explain select * from emp where emp.deptno in(select deptno from dept where dept.deptno>1000);
+----+--------------------+-------+-----------------+---------------+---------+---------+------+----------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+-----------------+---------------+---------+---------+------+----------+--------------------------+
| 1 | PRIMARY | emp | ALL | NULL | NULL | NULL | NULL | 10000000 | Using where |
| 2 | DEPENDENT SUBQUERY | dept | unique_subquery | PRIMARY | PRIMARY | 4 | func | 1 | Using index; Using where |
+----+--------------------+-------+-----------------+---------------+---------+---------+------+----------+--------------------------+
但是一旦in中使用了子查询,而且是不相关子查询,我们发现外层的查询是不会使用索引的,但是内层的查询是使用索引了的。
这个时候如果把in换成exists,也不会使用索引,而且效率奇慢 select * from emp where EXISTS (select 1 from dept where dept.deptno>1000 and emp.deptno = dept.deptno) 。应该是这种写法形成了相关子查询,所以效率低。
explain select * from emp where EXISTS (select 1 from dept where dept.deptno>1000 and emp.deptno = dept.deptno);
+----+--------------------+-------+--------+---------------+---------+---------+--------------------+----------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+--------+---------------+---------+---------+--------------------+----------+--------------------------+
| 1 | PRIMARY | emp | ALL | NULL | NULL | NULL | NULL | 10000000 | Using where |
| 2 | DEPENDENT SUBQUERY | dept | eq_ref | PRIMARY | PRIMARY | 4 | testdb1.emp.deptno | 1 | Using where; Using index |
+----+--------------------+-------+--------+---------------+---------+---------+--------------------+----------+--------------------------+
下面试一下这种情况,in中的子查询为大表
select * from dept where dept.deptno in (select deptno from emp where emp.deptno<1000)
这个查询in中的数据有一万多条,但是因为内查询使用了索引,而外表的数据又很少,所以查询相当快。
综上所述:in的效率是有保证的,并不是用exists替代in就能提高查询效率的,具体要看执行计划。当使用in查询,且外表数据过的时候,最好能将两个查询拆开。这样虽然在程序中进行了两次查询,但是效率提高了很多。
如上所述第二种和第三种使用in的方式,第二个查询只需0.003秒。第三个查询却需要25秒左右