=),后面跟 NOT IN / != / IS NOT NULL → 前面能命中索引,后面失效。表结构:

CREATE TABLE user ( id INT PRIMARY KEY, age INT, name VARCHAR(50), INDEX idx_age(age));
这些 SQL 索引一定失效:
SELECT * FROM user WHERE age != 18;SELECT * FROM user WHERE age NOT IN (18, 19, 20);SELECT * FROM user WHERE age IS NOT NULL;
原因:
MySQL 优化器认为:否定条件意味着要查询表中的大部分数据(高选择性差),直接进行全表扫描比走索引回表再过滤更快。
表结构:
CREATE TABLE user ( id INT PRIMARY KEY, name VARCHAR(50), age INT, INDEX idx_name_age(name, age) -- 联合索引);
SQL:
SELECT * FROM userWHERE name = '张三' AND age != 18;
结果:
索引只用到 name(等值匹配),age 用不上!
执行过程:
name = '张三' 在索引 idx_name_age 中快速定位到所有相关记录。age != 18(无法再利用索引进行高效过滤)。只有一种情况:否定条件过滤后,剩下的数据极少极少。
示例:
假设 status 字段只有 0 和 1,且表中 99% 的数据是 status=1。
SELECT * FROM order WHERE status != 1;
这种情况下,MySQL 可能会走索引(因为需要扫描的数据量很小)。但这种数据分布在业务中几乎遇不到!
IS NOT NULL 一定无法有效使用索引。WHERE email IS NOT NULL
IS NOT NULL → 全表扫描。IS NOT NULL 才会走索引。(现实中极少)NOT IN / != / IS NOT NULL → 索引必失效。-- 原语句(索引失效)SELECT * FROM user WHERE age NOT IN (18);-- 优化后(可能走 range 索引扫描)SELECT * FROM user WHERE age > 18 OR age < 18;-- 注意:OR 条件可能导致索引失效,需结合实际情况看执行计划
-- 原语句(全表扫描)SELECT * FROM user WHERE age != 18;-- 优化后(可能走 index 索引扫描,比全表快)SELECT age FROM user WHERE age != 18;-- 因为 age 字段就在索引 idx_age 中,无需回表
用 LEFT JOIN / NOT EXISTS 代替:
-- 查找没有订单的用户SELECT u.* FROM user uLEFT JOIN order o ON u.id = o.user_idWHERE o.id IS NULL;-- 或SELECT * FROM user uWHERE NOT EXISTS (SELECT 1 FROM order o WHERE o.user_id = u.id);
用状态字段代替(0/1): 设计表时,对于是否、开关等字段,尽量使用 TINYINT 表示状态,查询时使用 status = 0 而非 status != 1。
OR 连接无索引字段 = 一边有索引,一边没索引 → 整条 SQL 索引直接失效!
MySQL 的规则很简单:只要 OR 两边任意一个字段没有索引,整个 WHERE 条件就无法使用索引,直接全表扫描。
假设你有一张用户表:
CREATE TABLE user ( id INT PRIMARY KEY, name VARCHAR(50), phone VARCHAR(20));
你只给 name 建了索引,phone 没建索引:
CREATE INDEX idx_name ON user(name); -- name 有索引-- phone 无索引
现在你写了这条 SQL:
SELECT * FROM user WHERE name = '张三' OR phone = '13800138000';
结果:索引完全失效!
明明 name 有索引,但因为 OR 后面的 phone 没有索引,MySQL 只能:
全表扫描每一行,判断 name 或 phone 是否满足条件
WHERE 有索引字段 = '值' OR 无索引字段 = '值'-- 结果:索引失效
WHERE 无索引字段1 = '值' OR 无索引字段2 = '值'-- 结果:本来就没索引,全表扫描
CREATE INDEX idx_a_b ON table(a, b); -- 联合索引WHERE a = 1 OR c = 2-- 结果:c 无索引 → 失效
OR 两边的字段 都 有 独 立 索 引!
例子:
CREATE INDEX idx_name ON user(name);CREATE INDEX idx_phone ON user(phone); -- 两个字段都有独立索引SELECT * FROM user WHERE name = '张三' OR phone = '13800138000';
✅ 这种情况 MySQL 会使用:index_merge(索引合并)
两边分别走索引,再合并结果,不会失效。
你以后写 SQL 记住这个口诀:
AND 可以混,OR 必须全!
只要 SQL 中出现 NOT IN、!=、IS NOT NULL 这类否定条件,就默认索引不会生效,除非数据分布极端特殊(否定后数据量极少)。在设计和优化时,应尽量避免直接使用这类条件。