写SQL的时候,经常碰到要查某个值在不在另一张表里。这时候很多人纠结:到底用 IN 还是 EXISTS 效率更高?其实这事儿真没标准答案,得看你的具体场景。
先看个常见例子
比如你是个电商后台开发,现在要查出所有买过某类商品的用户。订单表 orders 很大,用户表 users 相对小一些。你想从 users 里筛选出那些在 orders 表里有记录的用户。
写法一,用 IN:
SELECT * FROM users WHERE user_id IN (SELECT user_id FROM orders WHERE product_type = 'phone')
写法二,用 EXISTS:
SELECT * FROM users u WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.user_id AND o.product_type = 'phone')
什么时候 IN 更快?
当子查询结果集很小,而且已经加了索引,IN 其实挺快的。数据库会先把子查询跑出来,变成一个简单的值列表,然后主查询直接去匹配。这种情况下,IN 走的是哈希查找,效率不低。
但要是子查询返回几千上万条数据,IN 就容易翻车。有些数据库对 IN 的列表长度有限制,而且处理大列表时性能明显下降。
EXISTS 适合啥情况?
EXISTS 是“有没有”的逻辑,它一旦找到一条匹配就停止搜索,属于“短路”操作。特别是主表数据少、关联表大的时候,EXISTS 往往更高效。
比如上面那个例子,users 表只有几万人,orders 表上亿条记录。用 EXISTS 配合索引,能快速判断每个 user 是否有符合条件的订单,不用把所有订单 ID 先捞出来。
别忘了索引这个关键因素
说白了,IN 和 EXISTS 谁快,很大程度取决于有没有索引。如果 orders.user_id 没有索引,不管你用哪个都慢得像爬。加上索引之后,两者性能差距往往就没那么明显了。
另外,现代数据库优化器越来越聪明。很多情况下,你写 IN,它自动帮你转成类似 EXISTS 的执行计划,反过来也一样。所以不用太焦虑语法本身。
简单粗暴的参考建议
小数据量随便写,差别不大。主表小、子查询表大,优先试 EXISTS。子查询结果很短,且有索引,IN 也没问题。不确定的时候,直接在生产环境类似的数据库上跑一下执行计划,看 cost 和 rows 准没错。
别听网上一棍子打死说“永远用 EXISTS”,技术这东西,讲究的是灵活应变。