not 遇 null 返回 unknown 而非 false,导致 where 过滤失效;正确用法需括号包裹条件、优先选 not exists 替代 not in,并显式处理 null。

NOT 本身没问题,但一碰 NULL 就容易查不到数据——不是语法错,是三值逻辑在生效。
NOT (a AND b) 不等于 “既不 a 也不 b”
想排除「状态是 draft 且作者是 admin」的记录,写 NOT (status = 'draft' AND author = 'admin') 才对。漏括号写成 NOT status = 'draft' AND author = 'admin',实际执行的是 (NOT status = 'draft') AND author = 'admin',语义完全偏移。
常见错误现象:WHERE NOT status = 'draft' OR NOT author = 'admin' 看似“两个都不满足”,实则只要任一条件不成立就命中,几乎返回全表。
- 多条件排他必须用括号包裹原始组合,再整体加
NOT - 复杂逻辑优先改写为显式否定,比如用
status != 'draft' OR author != 'admin'替代NOT (status = 'draft' AND author = 'admin'),语义更可控 - 注意
NOT (col = 'x')对col IS NULL的行返回UNKNOWN,不会被WHERE选中——这不是没生效,是 SQL 三值逻辑的正常行为
NOT IN 遇到 NULL 就失效,不是 bug 是标准
'a' NOT IN ('b', NULL) 等价于 'a' != 'b' AND 'a' != NULL,而 'a' != NULL 永远是 UNKNOWN,整个条件不成立,结果为空集。
使用场景:批量剔除已知 ID 列表、禁用用户名、黑名单邮箱域等。
- 子查询务必加
WHERE id IS NOT NULL过滤,例如:id NOT IN (SELECT id FROM blacklist WHERE id IS NOT NULL) - 确定右值不含
NULL时,NOT IN ('a','b','c')可用;否则一律改用NOT EXISTS - 性能上,
NOT IN在大表 + 无索引列时可能比LEFT JOIN ... IS NULL慢,尤其 MySQL 5.7 及以前版本
NOT EXISTS 是更安全、更贴近意图的替代方案
NOT EXISTS 既绕过 NOT IN 的 NULL 缺陷,又通常能利用关联字段索引加速,语义也最接近“不存在对应记录”。
示例:排除黑名单用户
SELECT * FROM users t WHERE NOT EXISTS ( SELECT 1 FROM blacklist b WHERE b.user_id = t.id );
-
NOT EXISTS不受右表NULL影响,子查询里即使有NULL也不改变外层逻辑 - 关联字段(如
b.user_id)若有索引,数据库通常能高效走索引查找 - 相比
NOT IN,NOT EXISTS在语义上更明确,维护时不容易误读
NULL 不能用 = 或 != 判断,必须用 IS NULL / IS NOT NULL
WHERE email = NULL 永远不返回任何行,因为 NULL = NULL 结果是 UNKNOWN,而 WHERE 只保留 TRUE 行。
想查“年龄明确不等于 25”的人,用 age != 25 即可;想把 NULL(未知年龄)也包含进来,就得写 age != 25 OR age IS NULL。
-
NOT (age = 25)和age != 25行为一致,但都**不包含NULL行** - 要显式包含
NULL,必须单独补OR age IS NULL,不能依赖NOT自动覆盖 -
NOT IN、NOT LIKE、NOT EXISTS各自对NULL的处理机制不同,不能凭直觉类推
最常被忽略的点是:NOT 本身不“怕” NULL,怕的是你默认它会把 NULL 当成 FALSE 处理。SQL 的三值逻辑里,NOT UNKNOWN 还是 UNKNOWN——这一条规则贯穿所有含 NOT 的表达式,从单列比较到子查询嵌套,无一例外。










