MySQL中的in和exists的区别
从效果上看:
- IN运算符:是看某个值是否存在于一个集合里。这个集合可以是静态列表,也可以是子查询。
- EXISTS运算符:用于判断一个子查询是否有结果,他只关心子查询是否返回了至少一行结果,不关心具体返回什么。
从性能上看:
- IN从语义上来说,是会先执行子查询,然后把列表确定了,之后再用外层表的每一行记录去看某个字段的值是否IN的列表里面存在,存在就返回。比如select * from A where A.id in (select B.a_id from B),会使用A表的每一行记录去执行select 1 from B where B.id=A.id,因为in的字段是可以使用索引的(这里的A.id可以使用索引),所以适合外层表有相关字段有索引的情况。有索引时,外层表的相关字段有索引时相当于是走
**range**
扫描,性能极高(类似多个**=**
查询的合并)。但是这个也不一定,因为MySQL优化器可能会进行优化。比如SELECT * FROM users WHERE id IN (SELECT user_id FROM orders WHERE status = ‘paid’);MySQL在IN里面是子查询,并且子查询是的user_id字段有索引,那么可能 会 将**IN**
转换为**JOIN**
或**EXISTS**
(这个要看优化器的选择了)。 - EXISTS 从语义上来说,他是外层表驱动内层表的,即,EXISTS外层表的每一行都会去执行子查询,所以外层的表数量要尽量少一些,内层表的查询速度要尽量快一些。比如select * from A where exist(select 1 from B where B.id=A.id),会使用A表的每一行记录去执行select 1 from B where B.id=A.id,只要存在一个能匹配的数据,就返回A的这一行数据。exists时候外层数据量少的,子查询比较大的,并子查询中可以利用索引的情况。这样的话,效率也不会低,因为外层循环少,内层可以利用索引,就算数据量大,但是查询也不慢,所以速度快。
但是实际上的性能不一定是上面分析的这样的,因为MySQL的优化器可能会对他们进行优化,你写的in可能会被优化为exist,所以谁效率高谁效率低还是要看实际的执行效果的。
注意:尽量不要使用not in,因为not in后面的集合中,如果存在null,那么结果一定是空。所以可能会导致你判断错误,你给他一个列表,想拿到列表中存在的数据,但是不小心放进去一个null值,结果一条数据也找不到了。
比如
-- 使用 IN:查找有订单的用户
SELECT * FROM sys_user
WHERE user_id IN (SELECT user_id FROM sys_order);
/* 结果:Alice (1), Bob (2)说明:IN 忽略子查询中的 NULL 值 */-- 使用 EXISTS:查找有订单的用户
SELECT * FROM sys_user u
WHERE EXISTS (SELECT 1 FROM sys_order o WHERE o.user_id = u.user_id
);
/* 结果:Alice (1), Bob (2)说明:EXISTS 同样忽略 NULL */
-- ⚠️ 危险示例:使用 NOT IN(含 NULL)
SELECT * FROM sys_user
WHERE user_id NOT IN (SELECT user_id FROM sys_order);
/* 结果:空集!(即使 Charlie 没有订单)原因:子查询包含 NULL => 整个条件无效 */-- ✅ 安全方案:使用 NOT EXISTS
SELECT * FROM sys_user u
WHERE NOT EXISTS (SELECT 1 FROM sys_order o WHERE o.user_id = u.user_id
);
/* 结果:Charlie (3)说明:正确找到无订单用户 */
结论:
"NOT IN 后的集合如果存在 NULL,结果一定是空"
"永远用 NOT EXISTS 替代 NOT IN"