ON子句中使用函数会导致索引失效,因MySQL无法在B+树索引上直接计算函数值,只能全表扫描;替代方案是改用范围查询、冗余字段或显式条件,避免函数调用。

ON子句里用函数会让索引失效
因为MySQL无法在索引B+树上直接执行函数计算,只能退化为对被驱动表的全行遍历。
-
YEAR(t2.create_time) = YEAR(t1.year)这类写法会让t2.create_time上的索引完全失效——优化器没法拿“2025”去索引树里查,它得先把每行的create_time算出年份再比,等于逐行读取整列 - 同理,
UPPER(t2.name) = t1.name、CONCAT(t2.prefix, t2.id) = t1.code都会触发type: ALL或type: index,EXPLAIN里能看到Extra字段出现Using where但没Using index - 字符集隐式转换也属于“函数行为”:比如
t1.name是utf8mb4_0900_as_cs,t2.name是utf8mb4_general_ci,MySQL会在比较前自动调用转换函数,结果一样不走索引
替代方案不是“改写函数”,而是提前物化
别想着把YEAR()搬到WHERE里或者加函数索引——MySQL 5.7不支持函数索引,8.0+虽支持但维护成本高,且多数场景没必要。
- 把时间范围拆成显式区间:
t2.create_time BETWEEN '2025-01-01' AND '2025-12-31',这样能走create_time上的普通索引 - 业务层预计算并存入冗余字段:加一列
create_year INT,建索引,JOIN条件直接用t2.create_year = t1.year - 实在要按月/周聚合,优先考虑
DATE_FORMAT(t2.create_time, '%Y-%m')→ 改成t2.create_time >= '2025-07-01' AND t2.create_time < '2025-08-01'
EXPLAIN里一眼识别这类问题
重点盯type和key两列,而不是只看rows估算值。
- 如果
type是ALL或index,而key显示NULL或明显不是你期望的索引名,基本就是函数/类型转换导致的失效 -
Extra里出现Using where; Using join buffer (Block Nested Loop)是典型信号:被驱动表没走索引,已降级为BNL算法 - 用
SHOW INDEX FROM t2确认索引存在且字段顺序正确,再检查SHOW CREATE TABLE t2里的字符集和排序规则是否与关联表一致
create_time建了索引,只要ON里套了函数,那索引就形同虚设。优化不是从“怎么让函数走索引”出发,而是从“怎么不用函数”出发。

















