对索引列使用函数会导致索引失效,因为B+树只存储原始值而非函数结果;如WHERE DATE(create_time) = '2024-01-01'需逐行计算,无法利用索引有序性,优化器被迫全表扫描;应改用WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02'。

对索引列使用函数会导致索引失效,根本原因不是数据库“不支持”,而是B+树索引压根没法匹配函数计算后的结果——它只存原始值,不存函数输出。
WHERE中调用DATE()、YEAR()等日期函数会绕过索引
比如WHERE DATE(create_time) = '2024-01-01',数据库必须对每一行的create_time先执行DATE(),再比对,无法利用索引的有序结构定位。优化器看到的是“对索引列做了不可下推的运算”,只能退化为全表扫描。
- ✅ 正确改法:
WHERE create_time >= '2024-01-01' AND create_time (左闭右开,精度可控) - ❌ 避免
BETWEEN '2024-01-01' AND '2024-01-01 23:59:59':秒级精度易漏数据,且时间类型可能含微秒 - ⚠️ PostgreSQL/Oracle虽支持
CREATE INDEX ON t(DATE(create_time)),但写入性能下降,别轻易建
SUBSTR()、UPPER()等字符串函数破坏前缀匹配能力
WHERE SUBSTR(name, 1, 3) = 'adm'无法走name上的普通索引,因为B+树按完整字符串排序,函数截取后失去位置映射关系。
- ✅ 优先转为前缀匹配:
WHERE name LIKE 'adm%'(可走索引) - ✅ 大小写不敏感场景,建索引时指定校对规则更可靠,如MySQL的
utf8mb4_0900_as_cs - ⚠️ MySQL 5.7及以前不支持函数索引,
UPPER(name)查询只能靠冗余字段或应用层处理
隐式转换本质也是“对索引列加了函数”
表面看没写函数,但WHERE user_id = 123(user_id是VARCHAR)会让MySQL自动执行CAST(user_id AS SIGNED),等效于在索引列上套函数。
- ✅ 统一类型:
user_id是字符串就写'123',是数字就别加引号 - ⚠️ MySQL有时对
id = '2'(id是INT)仍能走索引,这是兼容行为,PostgreSQL/Oracle直接报错或强制全表扫描 - ? 自查方法:执行
EXPLAIN,看key是否为NULL,Extra是否出现Using where; Using index以外的内容
真正容易被忽略的点是:函数索引(MySQL 8.0+、PostgreSQL)不是“万能解药”。它让查询变快,但每次INSERT/UPDATE都要多算一次函数值并写入索引页——高写入场景下,这个代价可能远超读取收益。

















