为什么MySQL使用NOT IN会出现全表扫描以及怎样重构?

阿磊小哥_3604

阿磊小哥_3604

2026-10-14

218人浏览

原创

not in 必然触发全表扫描且结果可能为空,因其语义遇null即判unknown而被where过滤;应改用left join + is null,并确保右表子查询排除null、连接字段类型一致、无隐式转换。

为什么mysql使用not in会出现全表扫描以及怎样重构?

NOT IN 几乎必然触发全表扫描,且结果可能为空——这不是配置或索引问题,而是语义和执行逻辑决定的;必须改用 LEFT JOIN + IS NULL 重写。

NOT IN 遇到 NULL 就查不到数据,还让索引失效

只要子查询返回任意一行的连接字段为 NULL(比如 SELECT id FROM t2 中有一条 id IS NULL),整个 NOT IN 表达式就判为 UNKNOWN。而 SQL 标准规定:WHERE 只保留 TRUE 行,UNKNOWN 和 FALSE 全被过滤。结果就是——一条不返回,DELETE 一条不删。

  • 优化器不敢对含 NULL 的子查询下推索引,直接放弃使用左表上的索引
  • 即使你给 t2.id 加了索引,MySQL 5.7 及更早版本基本无视;8.0 虽支持物化,但只要统计信息不准或子查询不可物化,仍退化为全表扫描
  • EXPLAIN 中常见 type: ALL、key_len: NULL、Extra: Using where; Using join buffer,Handler_read_rnd_next 动辄百万级

为什么加了索引,NOT IN 还是走全表扫描

B+ 树索引擅长“定位存在”,不擅长“确认不存在”。NOT IN 要求逐行比对是否“不在集合中”,没有起始/终止边界,无法利用树结构跳过数据块,本质上等价于全索引扫描甚至回表全查。

MySQL
MySQL

编写正确的MySQL查询,避免字符集、索引和锁方面的常见陷阱。

下载
  • IN 可走 INDEX RANGE SCAN,查几个值很快;NOT IN 没有范围概念,优化器无法导航子树
  • 子查询若没加 WHERE 过滤、返回行数多,优化器倾向选择最保守的嵌套循环策略
  • 隐式类型转换(如左表 INT、右表 VARCHAR)会让索引彻底失效,连 LEFT JOIN 都救不回来

安全重构:用 LEFT JOIN + IS NULL 替代 NOT IN

正确写法不是语法替换,而是守住三个关键点:

  • 右表子查询必须显式排除 NULL:(SELECT id FROM t2 WHERE id IS NOT NULL),否则 LEFT JOIN 后 t2.id IS NULL 会把本该匹配成功的行也留下
  • 连接条件必须是纯等值,不能带函数或转换,例如 ON t1.uid = t2.uid 可以,ON t1.uid = CAST(t2.uid AS CHAR) 不行
  • 左右表连接字段类型、字符集必须一致;若不一致,先统一再改写,否则索引照样不走
  • 原语句:DELETE FROM t1 WHERE id NOT IN (SELECT id FROM t2)
    安全改写:DELETE t1 FROM t1 LEFT JOIN t2 ON t1.id = t2.id WHERE t2.id IS NULL

容易被忽略的细节:LEFT JOIN 写法也会白忙一场

很多人以为只要换成 LEFT JOIN 就万事大吉,其实漏掉任一细节都会导致逻辑错误或性能不升反降:

  • 没在右表子查询里加 WHERE id IS NOT NULL,会导致左表中本应被排除的行因右表匹配到 NULL 而被误留
  • 连接字段类型不一致(比如 t1.id 是 BIGINT,t2.id 是 VARCHAR),MySQL 会做隐式转换,索引失效,执行计划仍可能退化为全表扫描
  • EXPLAIN 看到 type: ALL 且 rows 接近左表总行数,说明改写失败,得回头检查右表字段是否真非空、类型是否对齐

真正卡住性能的,往往不是数据量,而是那一行 id IS NULL 引发的语义坍塌和执行计划失控。重构时盯紧 NULL、类型、索引三件事,比调优参数管用十倍。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

4263

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

891

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

1109

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

6181

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

3023

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

6160

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

8321

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

1130

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

992

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL vs MySQL
PostgreSQL vs MySQL

共1课时 | 187人学习