对索引列做运算导致EXPLAIN显示type=ALL,因为B+树只存原始值,无法反向推导计算结果,优化器只能全表扫描逐行计算比对;正确做法是将运算移至常量侧,保持“索引列 op 常量”形式。

对索引列做运算为什么让 EXPLAIN 显示 type=ALL
因为 MySQL 的 B+ 树索引只存储列的原始值,不存储任何计算结果。当你写 WHERE age + 1 = 20 或 WHERE YEAR(create_time) = 2023,优化器无法把表达式“反向解出”对应哪些原始值落在索引页里——它得对每一行先算一遍再比,等价于全表扫描。
DATE()、UPPER()、CONCAT() 这些函数都踩同个坑
只要函数参数是索引列本身,索引就失效。不是函数“不支持”,而是索引结构根本不存函数输出值。
-
WHERE UPPER(name) = 'JOHN'→ name 索引失效 -
WHERE CONCAT(first_name, last_name) = 'JohnDoe'→ 即使 first_name 和 last_name 都有索引,也无效 -
WHERE create_time + INTERVAL 1 DAY > NOW()→ 运算在列上,索引失效
例外极少:MySQL 8.0+ 对某些内置函数(如 JSON_EXTRACT)配合生成列可走索引,但那是另一套机制,不能当成通用解法。
怎么改才真正生效:把运算从列移到常量侧
核心原则:让 WHERE 条件变成“索引列 op 常量”的形式,且 op 是能利用 B+ 树有序性的操作(=、>、>=、
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- ❌
WHERE DATE(create_time) = '2026-11-01'
✅WHERE create_time >= '2026-11-01 00:00:00' AND create_time - ❌
WHERE age * 2 = 40
✅WHERE age = 20 - ❌
WHERE score + bonus > 100
✅ 改成冗余字段total_score并建索引,或用生成列:ALTER TABLE t ADD total_score INT AS (score + bonus) STORED, ADD INDEX idx_total_score(total_score)
容易被忽略的隐含运算:隐式类型转换也算
看似没写函数,但类型不一致时 MySQL 会自动调用转换函数,效果等同于显式包裹。
- 列是
VARCHAR(20),条件传整数:WHERE phone = 13800000000→ 实际执行的是WHERE CAST(phone AS SIGNED) = 13800000000 - 列是
INT,条件传字符串:WHERE user_id = '123'→ 实际是WHERE user_id = CAST('123' AS SIGNED) - JOIN 字段字符集不同(如 utf8mb4 vs utf8),也会触发逐行转换
这类问题不会报错,EXPLAIN 里 key 是 NULL、type 是 ALL,但你可能根本没意识到发生了转换。










