MySQL对索引列使用函数(如DATE()、UPPER()、LEFT()、YEAR())会直接导致索引完全失效,优化器放弃走索引而全表扫描,因B+树存储原始值,函数操作破坏了索引键与查询条件的直接可比性。

MySQL 对索引列使用函数(如 DATE()、UPPER()、LEFT()、YEAR())会直接导致该列索引完全失效——不是“部分低效”,而是优化器彻底放弃走索引,降级为全表扫描。
为什么函数会让索引“看不见”
B+ 树索引里存的是字段的原始值(比如 create_time 存的是 '2026-09-03 14:22:05'),而 WHERE DATE(create_time) = '2026-09-03' 要求 MySQL 先对每一行执行 DATE() 函数,再比对结果。这意味着:索引无法提前跳过无关数据,也无法利用有序性做范围定位,只能逐行计算后判断。
本质上,函数操作破坏了「索引键值与查询条件可直接比较」的前提。
-
EXPLAIN中type会变成ALL,key显示NULL - 即使表只有几千行,
rows也会显示接近总行数 - 隐式转换(如
WHERE phone = 13800000000)也属于同类型问题,等价于在列上加了CAST()
如何安全地重写函数类查询
核心原则:让索引列以“裸值”形式出现在 WHERE 左侧,把计算逻辑移到右侧或改用范围表达。
- ❌
WHERE YEAR(create_time) = 2026→ ✅WHERE create_time >= '2026-01-01' AND create_time - ❌
WHERE LEFT(name, 4) = 'Jack'→ ✅WHERE name LIKE 'Jack%' - ❌
WHERE UPPER(email) = 'TEST@EXAMPLE.COM'→ ✅ 建议业务层统一存小写,查时用WHERE email = 'test@example.com';或 MySQL 8.0+ 创建函数索引:CREATE INDEX idx_email_lower ON users ((LOWER(email))) - ❌
WHERE price * 1.1 > 100→ ✅WHERE price > 100 / 1.1
哪些函数操作特别容易被忽略
除了显式函数,以下写法同样触发失效,且不易察觉:
- 隐式类型转换:
WHERE phone = 13800000000(phone是VARCHAR)→ 必须写成'13800000000' - 时间戳转字符串:
WHERE create_time = '2026-09-03'(没带时分秒)→ MySQL 会隐式补全,但可能触发类型转换,稳妥写法是用范围 - JSON 字段取值:
WHERE JSON_EXTRACT(data, '$.status') = 'active'→ 即使data有索引,提取后也不走索引;应考虑冗余普通字段或生成列索引
真正难的不是记住哪些函数不能用,而是在 ORM 或动态拼 SQL 场景下,参数类型、字符串引号、函数位置这些细节极易被掩盖。上线前务必用 EXPLAIN 验证真实执行计划,别信“看起来应该走索引”。


















