impossible where 是 mysql 5.7 在执行 explain 前真实查询 const 表(主键/非空唯一索引 + 等值常量)后确认无匹配记录而得出的结论,属于 early pruning 优化,并非推测;仅适用于单行确定性查找,不适用于 like、in 等多行条件,且依赖类型严格匹配(如 varchar 字段必须加引号)才能触发该路径。

MySQL 5.7 中 Impossible WHERE 是优化器真查了数据才下的结论
这个提示不是靠猜,也不是靠统计信息推断出来的。MySQL 5.7 在执行 EXPLAIN 前,如果遇到满足「const table」条件的表(主键/非空唯一索引 + 等值常量),会真的去磁盘或缓冲池里查一次那条记录。查不到,就直接判定整个 WHERE 不可能成立。
- 这种行为只发生在单行确定性查找场景:比如
WHERE id = 123,而id是主键或非空唯一索引 - 它不适用于
WHERE name LIKE 'abc%'或WHERE status IN (1,2,3)这类可能返回多行的条件 - 所以你看到
Extra: Impossible WHERE noticed after reading const tables,说明 MySQL 已经访问过索引 B+ 树叶子节点,确认无匹配项
注意:在 MySQL 5.6 及更早版本中,同样逻辑会报错中断;5.7 改为静默返回空结果,并在 EXPLAIN 的 Extra 字段留下这条提示。
WHERE 条件矛盾的典型写法
很多看似“合理”的 SQL,在 MySQL 5.7 的 const 表优化路径下会触发这个提示:
-
SELECT * FROM users WHERE id = 999999 AND deleted_at IS NOT NULL
→ 如果id = 999999根本不存在,deleted_at条件甚至不会被评估 -
SELECT * FROM orders WHERE order_no = 'ORD-000001' AND status = 0
→ 若order_no是唯一索引但该单号不存在,整个 WHERE 被判为 impossible SELECT * FROM products WHERE sku = 'ABC123' AND price <br> → 即使 <code>price 本身违反 CHECK 约束,MySQL 也不依赖约束元数据,而是先查 <code>sku是否存在
关键点在于:只要第一个 const 表查不到,后续所有条件都不再参与计算 —— 这不是 bug,是 MySQL 5.7 引入的「early pruning」优化。
为什么去掉引号反而让 EXPLAIN 显示正常?
这是最常被忽略的隐式类型转换陷阱:
- 表结构:
phone VARCHAR(20),有唯一索引 - 错误写法:
WHERE phone = 13800138000
→ MySQL 把数字13800138000转成字符串比较,但转换过程可能截断或格式不一致(如科学计数法),导致无法命中唯一索引 →type降级为ALL或index,也就不会走 const 表路径 → 不触发Impossible WHERE - 正确写法:
WHERE phone = '13800138000'
→ 类型严格匹配,能走唯一索引 → 触发 const 查找 → 若该号码不存在,就出现Impossible WHERE
所以不是“去掉引号让优化生效”,而是“加引号后才真正触发 const 表优化路径”,进而暴露出数据缺失问题。
调试和规避建议
- 先用
SELECT COUNT(<em>)</em>验证 const 条件是否真能查到数据:SELECT COUNT() FROM users WHERE id = 12345; - 拆解复杂 WHERE:把子查询、JOIN 条件暂时注释掉,单独测试 const 表部分
- 检查字段类型与传入值是否完全一致,尤其注意:
-
VARCHAR字段必须用单引号 -
ENUM或SET字段要核对合法值列表 - 数字型主键不要传字符串(除非明确 cast)
-
- 如果业务上允许空结果,这个提示可忽略;但如果本应有数据却提示 impossible,说明数据已丢失或同步异常
真正容易被忽略的是:这个提示只在 const 表路径下出现,而 const 表路径本身依赖于索引可用性、类型一致性、以及值是否真存在于表中 —— 它既是优化特征,也是数据健康度的侧写信号。











