MySQL IN和NOT IN的效率对比,该用哪一个

例子

IN 2000 和 NOT IN 200 的对比

你好!作为一个和MySQL打过多年交道的"老司机",我非常乐意用最通俗的方式帮你解开这个疑惑。

先抛结论:在绝大多数情况下,IN 2000个元素 远比 NOT IN 200个元素 高效!

虽然2000看起来比200大了很多,但在数据库的世界里,"找存在"和"找不存在"的底层逻辑是完全不同的。这就好比"在人群中找2000个熟人"和"在一个200人的小团体里排除所有人找陌生人",前者的难度远远小于后者。

我们分三个维度来扒开它的底层逻辑:

维度一:执行逻辑的本质差异(相交 vs 求差)

  1. IN 是"找交集"(正向查找)
    IN (1, 2, ..., 2000) 的逻辑是:"把满足这2000个条件的数据给我找出来"。

数据库在扫描表的时候,每读到一行数据,只要它的值在这2000个元素里,立刻命中,加入结果集。

这就像你拿着一份2000人的VIP名单在门口检票,来一个人对一下名单,是VIP就放行,非常直接。

  1. NOT IN 是"求差集"(反向排除)
    NOT IN (1, 2, ..., 200) 的逻辑是:"除了这200个人,其他人全给我找出来"。

数据库在扫描表时,每读到一行数据,必须确认它绝对不在这200个元素里,才能加入结果集。

更致命的是,NOT IN 天生无法高效利用索引。因为索引是根据B+树排好序的,适合"精准找人",而不适合"排除一批人后找剩下的人"。

维度二:为什么 IN 2000 不慢?

很多人觉得2000个元素太多,会不会把SQL撑爆?其实不必太担心:

优化器会变身: MySQL很聪明,当 IN 列表里的元素不超过一定阈值时,它会把 IN 优化成 OR 连接;当元素较多时,它会自动把 IN 列表转换成一张临时内存表,然后和你的主表做哈希连接或者嵌套循环连接。这种临时表的查找速度是极快的。

索引完美生效: 如果查询字段有索引,MySQL会拿着这2000个值,直接在B+树的叶子节点上"定点狙击",范围再大也只是多扫描几次索引而已,效率依然很高。

维度三:为什么 NOT IN 200 很慢且危险?

NOT IN 不仅是效率问题,它还隐藏着巨大的"坑":

索引失效的幕后黑手: 假设你在 id 字段上建了索引。WHERE id IN (1,2...) 会走索引;但 WHERE id NOT IN (1,2...) 往往会导致优化器放弃索引,直接全表扫描!因为全表扫描可能比反复在B+树上跳跃找"不在这200个里的数据"成本更低。

NULL 的终极陷阱(重中之重): 如果 NOT IN 的那200个元素里,哪怕只有一个值是 NULL,整个查询将返回空结果!

为什么?逻辑上,id NOT IN (1, 2, NULL) 等价于 id != 1 AND id != 2 AND id != NULL。

在SQL里,任何值和 NULL 比较的结果都是 Unknown(未知),而 True AND Unknown 的结果还是 Unknown(相当于False)。于是,所有行都被过滤掉了。

为了避免这个Bug,你必须写成 WHERE id NOT IN (1, 2, NULL) AND id IS NOT NULL,这不仅丑陋,而且进一步拖慢了速度。

专家建议:实战中该怎么写?

既然 NOT IN 这么拉胯,那遇到"排除某些数据"的需求该怎么办?用 LEFT JOIN 或 NOT EXISTS 替代!

替代方案:NOT EXISTS(强推)

如果你要排除200个元素,最好这样写:

sql 复制代码
SELECT a.* 
FROM table_a a
WHERE NOT EXISTS (
    SELECT 1 
    FROM table_b b 
    WHERE a.id = b.id
);

为什么 NOT EXISTS 更好?

它遇到 NULL 不会出现整体失效的Bug。

优化器会将其改写为反半连接,它可以在扫描主表时,一旦在子查询中找到匹配项,立刻停止子查询的扫描(Early Exit),效率比 NOT IN 的全量比对高得多。

总结

效率对比:IN 2000 > NOT IN 200

原因:IN 是正向狙击,能走索引、能缓存临时表;NOT IN 是反向排除,容易全表扫描,且极易被 NULL 陷害导致结果全空。

行动指南:尽量避免使用 NOT IN,用 NOT EXISTS 或 LEFT JOIN ... WHERE b.id IS NULL 来替代。

下次再遇到类似的取舍,记住一句话:数据库最喜欢"告诉我你要什么(IN)",最讨厌"告诉我你不要什么(NOT IN)"。

相关推荐
zyseo85 小时前
谷歌SEO 准首页词挖掘脚本:找出排在第 8-20 名的隐藏流量
大数据·数据库·人工智能
xiaogai_gai7 小时前
仅查询策略实战:从金蝶云·星空旗舰版定时拉取收料通知单
大数据·数据库·python
数据工匠老o7 小时前
异构数据库DDL同步:260条链路的结构变更自动适配方案
数据库·架构
L0017 小时前
MySQL5.7 DATE 字段无法设置默认日期?用触发器搞定|个人任务清单项目实战
mysql
这个DBA有点耶8 小时前
银行业市占率最高的国产数据库是谁?先搞清楚“市占率”的四种口径
数据库·程序员·架构
用户6279947182629 小时前
南大通用GBase 8a之基于审计日志的冷温热数据分析
数据库
数据库小学妹9 小时前
数据库运维五级成熟度:监控、备份、高可用怎么串起来
运维·数据库·监控
一个天蝎座的程序猿9 小时前
为了给报表提速,我把金仓KES的列存储折腾了个遍:聊聊KES的技术能力
数据库
飞越未来云城的霓虹飞猪9 小时前
AI 数据库地图:从 PostgreSQL 到向量数据库,一次看懂 AI 时代的数据底座
数据库
不像程序员的程序媛9 小时前
导出mysql数据结构
数据库·mysql