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











