NOT仅在WHERE或HAVING中有效,必须修饰布尔表达式;对NULL需显式处理,NOT IN遇NULL返回空集,NOT条件常无法走索引,字符串排除应避免低效NOT LIKE。

NOT 用在 WHERE 子句里才能真正过滤数据
很多人写 NOT 却没效果,根本原因是把它放在了错误位置——比如塞进 SELECT 列表或 GROUP BY 里。它只在 WHERE 或 HAVING 中起作用,且必须搭配布尔表达式。
-
NOT本身不接受值,只能修饰条件:正确是WHERE NOT status = 'archived',错误是WHERE NOT 'archived' - 对空值(
NULL)要格外小心:NOT status = 'archived'会漏掉所有status IS NULL的行,因为NULL = 'archived'结果是UNKNOWN,取反仍是UNKNOWN,不被WHERE接受 - 如果字段可能为
NULL,得显式处理:WHERE status != 'archived' OR status IS NULL或更稳妥地用WHERE COALESCE(status, '') != 'archived'
NOT IN 容易因 NULL 意外丢数据
用 NOT IN 排除一批 ID 时,只要子查询结果里有一个 NULL,整条语句就返回空集——这是 SQL 标准行为,不是 bug。
- 例如:
SELECT * FROM orders WHERE id NOT IN (SELECT order_id FROM refunds),若refunds.order_id有NULL,结果永远为空 - 解决方法只有两个:要么提前排除
NULL:SELECT * FROM orders WHERE id NOT IN (SELECT order_id FROM refunds WHERE order_id IS NOT NULL) - 要么改用
NOT EXISTS,它对NULL不敏感:SELECT * FROM orders o WHERE NOT EXISTS (SELECT 1 FROM refunds r WHERE r.order_id = o.id)
NOT 和索引能不能配合好
大多数数据库对 NOT 条件不走索引,尤其是 NOT IN、NOT LIKE '%abc' 这类,执行计划里常看到全表扫描。
-
NOT status = 'active'在 PostgreSQL 或 SQL Server 上可能用上索引,但 MySQL 8.0 之前基本不用 - 如果排除的是小比例数据(比如只排除 2% 的记录),不如改写成正向条件 +
UNION ALL拆分,有时反而更快 - 真正想靠索引加速排除操作,优先考虑加覆盖索引,或者把“排除逻辑”下推到应用层做二次过滤(比如先查出要保留的 ID 集合)
字符串排除别直接 NOT LIKE
NOT LIKE 看似简单,但通配符位置一变,语义和性能全不同。
-
NOT LIKE 'A%'可能走索引(前缀匹配),而NOT LIKE '%A'或NOT LIKE '%A%'几乎肯定全表扫 - 想排除多个固定前缀?别堆
NOT LIKE 'A%' AND NOT LIKE 'B%',改用LEFT(col, 1) NOT IN ('A', 'B'),更容易命中索引 - 区分大小写要注意:PostgreSQL 默认区分,MySQL 取决于 collation;用
NOT ILIKE(PG)或NOT LIKE ... COLLATE utf8mb4_0900_as_cs(MySQL 8.0+)才可靠
实际写的时候,最常被忽略的是 NULL 对 NOT 逻辑的侵蚀——它不报错,也不提示,只是静默吞掉本该出现的行。

















