在做数据库优化的时候,经常有人问:我给某个字段加了索引,但如果在查询时对这个字段做了计算,比如加减乘除或者函数处理,那这个索引还能用上吗?答案是:大概率用不上。
举个实际场景
假设你有一张订单表 orders,里面有 create_time 字段,类型是 datetime,并且已经为它建立了索引。现在你想查某一天的订单:
SELECT * FROM orders WHERE YEAR(create_time) = 2024 AND MONTH(create_time) = 5;
看起来没问题,但问题就出在这儿——你在索引列 create_time 上用了函数 YEAR() 和 MONTH(),这就导致数据库引擎无法直接利用索引树去快速定位数据。
为什么不能用索引?
索引的本质是排序结构(比如B+树),它存储的是原始值的有序排列。当你对字段进行计算时,数据库必须先算出每个值的结果,再拿结果去比对条件。这就像图书馆的书按编号排序,但你要找“编号乘以2等于100的书”,管理员没法直接二分查找,只能一本本翻。
换句话说,索引失效的核心原因是:无法将计算后的结果映射回原始索引结构。
怎么写才能继续用索引?
还是上面的例子,正确的做法是让索引列保持“原样”,把计算放在右边:
SELECT * FROM orders WHERE create_time >= '2024-05-01' AND create_time < '2024-06-01';
这样 create_time 没有被任何函数包裹,数据库可以直接利用索引范围扫描,效率高得多。
常见“踩坑”操作
除了日期函数,下面这些写法也都会让索引失效:
WHERE price * 1.1 > 100—— 对字段做乘法WHERE UPPER(name) = 'ABC'—— 用函数转大小写WHERE id + 1 = 5—— 字段加常数
如果你真想走索引,就得改成:
WHERE price > 100 / 1.1
-- 或者
WHERE name = 'abc' -- 并确保字段存的就是小写
-- 或者
WHERE id = 4
有没有例外?
现代数据库有些优化手段可以绕过这个问题。比如MySQL 8.0支持函数索引,你可以专门创建一个基于表达式的索引:
CREATE INDEX idx_year_month ON orders ( (YEAR(create_time)), (MONTH(create_time)) );
这样前面那个带函数的查询就能用上索引了。不过这是特例,不是所有场景都适用,而且会增加维护成本。
所以日常开发中,最稳妥的方式还是:**别让索引列参与计算,让它干干净净地出现在 WHERE 条件左边**。
简单一句话:你对字段动手动脚,索引就可能撂挑子不干了。