WHERE中对字段用函数会使索引失效,因索引基于字段原始值有序构建,函数运算破坏“索引列独立出现”原则,迫使全表扫描;应改写为范围查询或避免左值函数。

WHERE 中对字段用函数为什么会让索引失效
因为 MySQL 无法直接拿函数结果去匹配索引 B+ 树的有序结构。索引是按字段原始值排序构建的,UPPER(name)、DATE(create_time) 这类操作会强制 MySQL 先计算每行函数结果,再比对——等于全表扫描后再过滤,优化器干脆放弃走索引。
常见踩坑的函数写法和等价改写
不是所有函数都必然失效,但多数单行函数在左值位置(即字段侧)都会导致索引失效。关键看是否破坏“索引列独立出现”原则:
-
WHERE YEAR(create_time) = 2024→ 改成WHERE create_time >= '2024-01-01' AND create_time -
WHERE UPPER(name) = 'ABC'→ 改成WHERE name = 'abc'(配合utf8mb4_0900_as_cs等大小写敏感校验),或建函数索引(MySQL 8.0+):CREATE INDEX idx_name_upper ON t1 ((UPPER(name))) -
WHERE SUBSTRING(phone, 1, 3) = '138'→ 改成WHERE phone LIKE '138%'(前提是前缀有区分度且字段有索引)
函数索引不是万能解,要注意版本和限制
MySQL 8.0 才支持函数索引(Virtual Generated Column + 索引组合),但有硬约束:
- 函数必须是确定性的(比如
NOW()不行,UPPER()可以) - 生成列不能是
BLOB或TEXT类型(SUBSTRING()返回类型需显式转为VARCHAR) - 函数索引不适用于
ORDER BY或GROUP BY中的表达式重用(仍可能触发 filesort) - 维护成本高:一旦业务逻辑变更函数表达式,索引就得重建
比函数更隐蔽的索引失效场景
有些写法看着没函数,实际也等效于隐式函数调用:
-
WHERE create_time + INTERVAL 1 DAY > NOW()→ 字段被运算,索引失效;应改为WHERE create_time > NOW() - INTERVAL 1 DAY -
WHERE status IN ('1', '2', '3')且status是TINYINT→ 字符串字面量触发隐式类型转换,可能使索引失效;统一用数字:WHERE status IN (1, 2, 3) -
WHERE name != ''在name有大量空字符串时,优化器可能认为选择率太低而放弃索引
函数索引和重写条件能解决大部分问题,但真正难的是识别那些“看起来合理、实际绕不开函数”的业务查询——比如模糊查中间段、多字段拼接搜索。这种时候得回到数据建模本身,而不是只盯着 SQL 怎么写。











