WHERE中对索引列用函数会导致索引失效,因为B+树只存储字段原始值而非函数结果;需将计算移至常量侧,如YEAR(create_time)=2024改写为create_time>='2024-01-01' AND create_time<'2025-01-01'。

WHERE中对索引列用函数为什么走不了索引
因为索引(B+树)只存字段原始值,不是函数结果。MySQL没法拿着UPPER(name)的输出去查索引树——它得先把每行name算一遍UPPER(),再比对,等于放弃索引、硬扫全表。
哪些函数写法会直接让索引失效
只要索引列出现在函数参数里或表达式左侧,基本就废了。常见有:
-
YEAR(create_time) = 2024→create_time索引失效 -
UPPER(name) = 'ABC'→name索引失效 -
SUBSTRING(phone, 1, 3) = '138'→phone索引失效 -
create_time + INTERVAL 1 DAY > NOW()→ 字段被运算,等效于函数 -
status IN ('1', '2')且status是TINYINT→ 隐式转换CAST(status AS CHAR),也失效
怎么改写才能继续用上索引
核心思路:把计算从字段侧移到常量侧,让索引列“裸奔”参与比较。
-
YEAR(create_time) = 2024→ 改成create_time >= '2024-01-01' AND create_time -
UPPER(name) = 'ABC'→ 若校对集支持大小写不敏感(如utf8mb4_0900_as_cs),直接用name = 'abc' -
SUBSTRING(phone, 1, 3) = '138'→ 改成phone LIKE '138%'(前提是phone有索引且前缀区分度够) - 实在绕不开函数?MySQL 8.0+ 可建函数索引:
CREATE INDEX idx_name_upper ON t1 ((UPPER(name))),但注意函数必须确定性、不能返回TEXT/BLOB
容易被忽略的隐式陷阱
有些写法看着没函数,实际一样失效:
-
WHERE phone = 13800138000(phone是VARCHAR)→ 触发CAST(phone AS SIGNED) -
WHERE id = '123'(id是INT)→ 触发CAST('123' AS SIGNED) - 联合索引
(a, b, c)中写WHERE a = 1 AND b > 10 AND c = 5→c条件不走索引,范围之后列全部失效
真正难的不是改SQL,而是识别那些业务上“非函数不可”的场景——比如按身份证中间四位模糊匹配、多字段拼接搜索。这时候光调优SQL没用,得回到数据建模,提前冗余字段或引入全文检索。


















