对索引列使用函数会导致索引失效,因索引按原始值排序而函数改变值形式;应改写为范围查询或等值查询,如用create_time >= '2026-01-01'替代year(create_time) = 2026。

WHERE子句里对列用函数会直接让索引失效
MySQL、PostgreSQL等主流数据库在执行WHERE条件时,如果对索引列施加了函数(比如YEAR(create_time)、UPPER(name)、DATE(updated_at)),优化器就无法利用该列上的索引进行快速定位。本质原因是:索引是按原始列值排序存储的,而函数改变了值的表达形式,数据库没法把函数结果和索引B+树结构对齐。
- 常见失效写法:
WHERE YEAR(create_time) = 2026、WHERE UPPER(email) = 'A@B.COM'、WHERE SUBSTRING(phone, 1, 3) = '138' - 对应可命中索引的改写:
WHERE create_time >= '2026-01-01' AND create_time 、<code>WHERE email = 'a@b.com'(配合大小写敏感索引或COLLATE utf8mb4_0900_as_cs)、WHERE phone LIKE '138%' - 某些函数在特定版本支持函数索引(如MySQL 8.0+的
CREATE INDEX idx_year ON t ((YEAR(create_time)))),但不是所有场景都适用,且维护成本高
隐式类型转换比显式函数更隐蔽、更容易被忽略
当WHERE中列的类型和传入参数类型不一致时(比如user_id是BIGINT,却写成WHERE user_id = '123'),数据库会自动把列转成字符串再比较——这个隐式转换过程同样绕过索引。现象上看起来只是加了单引号,实际执行计划里key字段为NULL,type变成ALL。
- 典型陷阱:
WHERE status = 1(status是VARCHAR)或WHERE mobile = 13800138000(mobile是VARCHAR) - 查证方式:用
EXPLAIN看key是否为空、Extra是否出现Using where; Using index以外的冗余提示 - 修复动作:统一参数类型,必要时在应用层做类型校验,或在SQL里显式转换(如
WHERE CAST(mobile AS SIGNED) = 13800138000,但不如源头修正)
LIKE带前置通配符本质上也是“列上套函数”的等效行为
LIKE '%abc'看着没调函数,但它等价于对整列做模式匹配扫描,数据库无法用索引的有序性跳过无关数据块,只能逐行检查。这和WHERE UPPER(name) = 'ABC'一样,都是让索引“看得见、用不上”。
- 能走索引的写法只有:
LIKE 'abc%'(前缀匹配)、LIKE 'abc_'(固定长度通配) - 模糊搜索需求强烈时,不要硬扛,考虑
FULLTEXT索引(MySQL)或pg_trgm扩展(PostgreSQL) - 注意
LIKE的字符集和排序规则影响匹配效率,例如utf8mb4_unicode_ci可能比utf8mb4_bin慢
EXPLAIN里看到Using filesort或Using temporary,往往也跟函数使用有关
比如ORDER BY UPPER(name)或GROUP BY DATE(created_at),即使name或created_at有索引,函数也会导致排序/分组无法复用索引顺序,数据库被迫建临时表或额外排序。这类问题在EXPLAIN输出里表现为Extra字段含Using filesort或Using temporary,且rows显著偏高。
- 验证方法:去掉函数再
EXPLAIN对比,看Extra是否消失、rows是否下降一个数量级 - 折中方案:如果业务强依赖大小写不敏感排序,可建生成列+索引(MySQL 5.7+):
ALTER TABLE users ADD COLUMN name_lower VARCHAR(64) STORED AS (LOWER(name)); CREATE INDEX idx_name_lower ON users(name_lower); - 真正棘手的是时间分组类需求,比如“按自然周统计”,
WEEK(created_at)无法走索引,得靠预计算汇总表或物化视图缓解
EXPLAIN里key是NULL、type是ALL、rows远超预期,第一反应就该检查WHERE和ORDER BY里有没有对列动了手脚。










