not 不优雅,易因 null、三值逻辑和索引失效引发意外结果或性能下降;应优先用 not exists 替代 not in,用显式否定替代 not (a and b),并对 null 显式判断。

直接说结论:NOT 本身不“优雅”,它容易因 NULL、三值逻辑和索引失效导致结果意外或性能骤降;真正稳健的排除方式是用 NOT EXISTS 替代 NOT IN,用显式否定替代嵌套 NOT (A AND B),并始终对空值做显式判断。
NOT IN 遇到 NULL 就失效,不是 bug 是标准行为
这是最常踩的坑:只要子查询或字面量列表里有一个 NULL,整个 NOT IN 表达式就恒为 UNKNOWN,WHERE 自动过滤掉所有行,结果集为空。
- 错误写法:
SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM orders)—— 若orders.user_id有NULL,查不到任何数据 - 临时补救:
SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM orders WHERE user_id IS NOT NULL) - 推荐解法:改用
NOT EXISTS,天然绕过 NULL 问题,语义更准,通常也更快
NOT LIKE 不等于 “反向模糊匹配”,NULL 和通配符要分开处理
NOT LIKE 不是 LIKE 的简单翻转,它对 NULL 字段返回 UNKNOWN,默认被 WHERE 排除;且不含通配符时(如 NOT LIKE 'abc')等价于 != 'abc',但语义模糊,应避免。
- 正确用法必须带通配符:
name NOT LIKE '%admin%'(排除含 admin 的)、path NOT LIKE '%\_tmp%' ESCAPE '\'(排除含字面量_tmp的) - 要保留
NULL记录?补条件:WHERE column NOT LIKE '%bad%' OR column IS NULL - 大小写敏感性不统一:PostgreSQL 默认区分,得用
NOT ILIKE;MySQL 取决于 collation;跨库别依赖NOT LIKE做一致性逻辑
NOT (A AND B) 容易逻辑翻车,优先拆成显式否定
NOT 作用于整个布尔表达式,不是日常语言里的“都不满足”。NOT (status = 'draft' AND score > 80) 等价于 status != 'draft' OR score ,而不是“既不是 draft 也不大于 80”。
- 想排除「状态是 draft 且分数 > 80」的记录,必须加括号:
WHERE NOT (status = 'draft' AND score > 80) - 漏括号写成
WHERE NOT status = 'draft' AND score > 80,实际是“非 draft 且分数 > 80”,语义完全偏移 - 更稳妥的写法是正向重写:
WHERE status != 'draft' OR score ,或直接用白名单思维:<code>WHERE status IN ('active', 'pending')
NOT 条件基本不走索引,尤其 NOT LIKE '%xxx'
绝大多数数据库对 NOT IN、NOT LIKE '%suffix'、NOT LIKE '%middle%' 无法有效使用索引,执行计划里大概率看到全表扫描。
-
NOT LIKE 'prefix%'有可能走前缀索引(取决于数据库和字段类型),但仍是例外而非规则 - 大表排除场景,优先考虑
NOT EXISTS(可利用关联字段索引)或LEFT JOIN ... WHERE right_table.id IS NULL - 如果必须用
NOT IN,确保右侧字段有索引,且提前过滤NULL;字面量列表超过几百项,应转为临时表或 CTE
真正难的不是写 NOT,而是预判它在 NULL、三值逻辑、索引和跨库兼容性上的连锁反应。每次写完 NOT 条件,都该问一句:这个字段可能为 NULL 吗?右边会不会出 NULL?有没有更直白的正向写法?











