MYSQL · SQL 学习资料
IN vs EXISTS:把 NULL 陷阱看明白
选择 SQL 类型,点击用户行,观察条件如何计算。你还可以开关订单中的 NULL,亲眼看到 NOT IN 与 NOT EXISTS 为什么不一样。
先纠正一个常见误区:
IN 的结果集里有 NULL,不代表 IN 永远不为 TRUE。只要找到明确匹配的非 NULL 值,仍然为 TRUE;真正危险的是 NOT IN 在列表含 NULL 时可能无法筛出任何行。NULL IN (NULL) 的结果也不是 TRUE,而是 UNKNOWN。① users(外表)
点击某一行试试| id | name | 当前 SQL 结果 |
|---|
查询目标:找到存在 / 不存在金额大于 100 的订单的用户。
② orders(内表)
amount > 100| order_id | user_id | amount | 是否满足金额条件 |
|---|
③ SQL 与判断步骤
1. 选中外表用户2. 判断子查询3. 得到 TRUE / FALSE / UNKNOWN
④ 最终查询结果
⑤ 一眼分清:NULL 真值表
IN (1, NULL)
1 IN (1, NULL) → TRUE
2 IN (1, NULL) → UNKNOWN
NULL IN (1, NULL) → UNKNOWN
2 IN (1, NULL) → UNKNOWN
NULL IN (1, NULL) → UNKNOWN
NOT IN (1, NULL)
1 NOT IN (1, NULL) → FALSE
2 NOT IN (1, NULL) → UNKNOWN
NULL NOT IN (1, NULL) → UNKNOWN
2 NOT IN (1, NULL) → UNKNOWN
NULL NOT IN (1, NULL) → UNKNOWN
EXISTS (SELECT NULL)
只要子查询返回一行 → TRUE
哪怕这一行的值就是 NULL
因为检查的是“有没有行”
哪怕这一行的值就是 NULL
因为检查的是“有没有行”
NOT EXISTS (无匹配行)
子查询没有返回行 → TRUE
子查询存在关联匹配行 → FALSE
不会把 NULL 当成相等
子查询存在关联匹配行 → FALSE
不会把 NULL 当成相等
⑥ 真实 MySQL 优化器与面试要点
不要机械背“IN 先全部查出来,EXISTS 每行执行一次”。这是便于理解的逻辑模型,不是 MySQL 必然采取的物理执行方案。现代 MySQL 可能对 IN/EXISTS 进行半连接(semijoin)、物化(materialization)、索引查找等优化,两者可能生成相似执行计划。
选择依据:先保证语义正确,再看索引、表大小、选择性和实际执行计划。用 EXPLAIN ANALYZE 检查,不要单纯按“子查询大就选 EXISTS”下结论。
特别注意:上方 EXISTS 的 o.user_id = u.id 遇到 NULL 不会匹配,但这和“EXISTS 完全不受 NULL 影响”是两回事。EXISTS 不检查 SELECT 列的值,但 WHERE 里的 NULL 比较仍服从 SQL 三值逻辑。