对索引列使用函数或运算会导致索引失效,因B+树仅存储原始值;如DATE(create_time)='2024-01-01'应改用create_time>='2024-01-01' AND create_time<'2024-01-02'。

WHERE 条件里对索引列用函数或运算,索引就失效了
MySQL 在执行查询时,如果 WHERE 子句中对已建索引的列做了计算(比如 YEAR(create_time)、id + 1 = 100、UPPER(name)),优化器通常无法使用该索引进行快速定位——它得先算出每一行的结果再比对,退化为全表扫描。
这不是 MySQL “不聪明”,而是 B+ 树索引只存储原始值,不存计算结果。你不能指望它按“年份”查,却只在磁盘上存了完整的 DATETIME。
哪些写法会让索引列计算失效
常见但危险的操作:
-
WHERE DATE(create_time) = '2024-01-01'→ 改用范围查询:create_time >= '2024-01-01' AND create_time -
WHERE id * 2 = 100→ 改为:id = 50 -
WHERE UPPER(username) = 'ADMIN'→ 建索引时用生成列或确保字段本身已统一大小写,查询直接用username = 'admin' -
WHERE status + 0 = 1(隐式类型转换)→ 确保字段类型和参数一致,避免status = 1写成status = '1'
把计算逻辑下推到应用层的实操要点
不是所有计算都能/该移出去,核心原则是:**索引列参与的谓词必须保持“裸值比较”**。应用层要承担更多前置计算责任:
- 时间范围由应用算好起止时间戳,传给 SQL 的是两个确定的
DATETIME字符串,而非让 MySQL 解析DATE() - 状态映射表(如前端传 “enabled” → 后端转成数据库里的
1)不要依赖 SQL 层做字符串匹配 - 分页偏移量由应用算好
limit和offset,而不是用ROW_NUMBER() OVER()再过滤(尤其在 MySQL 8.0 以下) - 模糊搜索若依赖前缀,用
LIKE 'abc%';若必须中间匹配(%abc%),别指望索引,提前在应用层判断是否走全文检索或缓存兜底
容易被忽略的隐式陷阱
有些“看起来没计算”的写法,照样让索引失效:
- 字符集/排序规则不一致:
utf8mb4_0900_as_cs列 vsutf8mb4_general_ci参数 → 比较前强制转换,索引失效 - JOIN 条件两边字段类型不同,比如
INT关联VARCHAR,MySQL 会把整型转字符串再比,左侧索引白建 - 使用
IS NULL或IS NOT NULL对非空索引列虽可能走索引,但若该列允许 NULL 且 NULL 值很多,实际性能未必好,不如明确业务语义后改用默认值 - 复合索引最左前缀没被用上:
INDEX (a, b, c),但查询只用了b = ?→ 索引完全失效
真正难的不是知道“别在 WHERE 里算”,而是每次写 SQL 时多问一句:这个字段在索引里存的是什么?我当前写的表达式,MySQL 能否直接拿索引页里的值去比?


















