NOT仅在WHERE/HAVING中生效,取反布尔表达式而非排除数据;误用位置、忽略NULL、滥用NOT IN会导致语义漏查;NOT条件基本不走索引,应优先用NOT EXISTS或正向条件优化。

NOT本身不“排除数据”,它只是取反布尔表达式;写错位置、忽略NULL、滥用NOT IN,都会导致查不到该查的行——不是语法错,是语义漏。
WHERE里用NOT,别塞进SELECT或GROUP BY
NOT只在WHERE或HAVING中生效,且必须修饰一个完整条件。它不能直接跟字段名,也不能当值用。
- ✅ 正确:
WHERE NOT status = 'archived'、WHERE NOT (age > 65 AND active = TRUE) - ❌ 错误:
SELECT NOT status FROM users(语法报错)、WHERE NOT 'archived'(缺少比较)、GROUP BY NOT category(无效位置) - ⚠️ 注意:
NOT status IS NULL合法,等价于status IS NOT NULL,但后者更直观
NOT column = 'X'会漏掉NULL行
因为NULL = 'X'结果是UNKNOWN,NOT UNKNOWN仍是UNKNOWN,而WHERE只保留TRUE行。
- 想查“所有非VIP用户,包括等级未填的”:用
user_level != 'VIP' OR user_level IS NULL - 想查“明确不是VIP的用户(不含NULL)”:用
user_level = 'VIP'再取反也行,但必须确认业务上NULL不算“非VIP” - 更稳妥的写法:
COALESCE(user_level, '') != 'VIP',把NULL转为空字符串再比
NOT IN子查询含NULL就返回空集
这是最隐蔽的坑:id NOT IN (SELECT ref_id FROM logs)只要logs.ref_id有一条是NULL,整条查询结果为空,哪怕其他值都匹配不上。
- 临时解法:在子查询里过滤NULL:
id NOT IN (SELECT ref_id FROM logs WHERE ref_id IS NOT NULL) - 推荐解法:改用
NOT EXISTS,天然免疫NULL:NOT EXISTS (SELECT 1 FROM logs l WHERE l.ref_id = t.id) - 关联排除场景也可用
LEFT JOIN ... WHERE right.id IS NULL,语义清晰且易优化
NOT条件基本不走索引,别指望靠它提速
多数数据库对NOT column = 'X'、NOT IN、NOT LIKE '%abc'几乎都不走索引,执行计划常显示全表扫描。
-
NOT LIKE 'A%'可能走前缀索引(取决于引擎),但NOT LIKE '%A'一定全扫 - 排除小比例数据(比如只剔除2%)时,不如改写成正向
IN或=条件 +UNION ALL拆分 - 真要高频排除,优先建覆盖索引,或把排除逻辑下推到应用层做二次过滤
真正麻烦的从来不是语法怎么写,而是你根本不知道字段里有没有NULL、子查询会不会吐NULL、索引到底用不用得上——这些点不提前验证,查出来为空时连错误提示都没有。

















