mysql函数索引必须与where表达式严格字面一致才能命中,包括大小写、空格、括号及函数名拼写;需用双层括号、显式cast(如json字段)、避免非确定性函数,并通过explain验证key和rows确认真实生效。

WHERE 子句里用了函数,比如 UPPER(name) 或 YEAR(create_time),普通索引完全不生效——这不是 MySQL 懒,是它压根没能力“猜”你想要索引哪个函数结果。解决办法只有一个:你得亲手建一个跟查询里一模一样的函数索引。
函数索引必须和 WHERE 表达式严格一致才能命中
MySQL 优化器不搞语义等价推导,只做字符串级匹配。建了索引,但查不到,八成是这里出问题:
- 索引定义是
((UPPER(name))),查询却写成WHERE upper(name) = 'ABC':部分 SQL mode 下大小写敏感,会不匹配 - 索引是
((TRIM(email))),查询却写成WHERE TRIM(LEADING ' ' FROM email) = 'a@b.com':表达式字面不等,直接放弃 - 索引用了
->(返回带引号 JSON 字符串),查询却用->>(去引号标量),类型不一致,隐式转换导致失效 - 多了一对空格、少了一个括号、函数名拼错(如
substring_index写成substr_index),建索引时可能成功,但优化器无法关联
验证是否真命中:看 EXPLAIN 的 key 字段是否显示你的索引名;再看 rows 是否显著小于全表行数——如果 rows ≈ 总行数,大概率只是“假走索引”,实际还是全表扫描后过滤。
JSON 字段建函数索引必须显式 CAST
JSON 列不能直接建 B+ 树索引。想按 data->'$.status' 查询?必须加类型声明:
- 单值提取(如 status、email):用
CAST(data->'$.status' AS CHAR(32)) - 数组元素匹配(如 courses[*].id):必须用
CAST(data->'$.courses' AS UNSIGNED ARRAY),否则报错
错误示例:CREATE INDEX idx_status ON t ((data->'$.status')) → 直接报 ERROR 3105
正确写法:CREATE INDEX idx_status ON t ((CAST(data->'$.status' AS CHAR(32))))
注意:-> 和 ->> 返回类型不同,函数索引中统一推荐用 -> + CAST,避免 ->> 在某些上下文触发隐式转换。
哪些函数能用,哪些绝对不行
函数索引只接受确定性(deterministic)且无副作用的函数:
- ✅ 可用:
UPPER()、TRIM()、SUBSTRING_INDEX()、DATE()、CAST(... AS ...)、JSON_EXTRACT() - ❌ 报错:
RAND()、NOW()、SYSDATE()、CONVERT_TZ()(含非常量参数时)、用户自定义函数(UDF) - ⚠️ 警惕:
MD5()、SHA2()虽然合法,但返回值太长(如 SHA2-512 是 128 字符),B+ 树索引效率低,慎用
另外,表达式不能含子查询、用户变量、临时表引用——这些在建索引阶段就会被拒绝。
建索引时最常踩的语法坑
DDL 对括号、大小写、空格极其敏感:
- 必须双层括号:
((UPPER(name)))✅,(UPPER(name))❌(缺外层) - 函数名别小写:
((upper(name)))在严格模式下可能失败,建议全大写保持兼容 - 别给整个表达式加引号:
((‘UPPER(name)’))❌,这是字符串字面量,不是函数调用 - 多列函数索引允许混用:
CREATE INDEX idx ON t ((UPPER(name)), created_at)✅,但第一列必须是函数表达式
最后提醒一句:函数索引本身不存数据,也不占额外磁盘空间,但它会让 INFORMATION_SCHEMA.STATISTICS 里多一条 Column_name = 'func' 的记录——查不到这条,说明索引根本没建成功。











