函数索引需将表达式结果固化进索引,必须用双括号语法如((UPPER(name))),仅支持确定性函数(如UPPER、DATE),不支持NOW等非确定性函数,且须MySQL≥8.0.13。

函数索引不是“给字段加个函数就能用”,而是必须让 MySQL 提前把表达式结果固化进索引结构里;没写对括号、用了不支持的函数、或版本低于 8.0.13,索引就压根不会生效。
CREATE INDEX 语句中双括号是硬性语法要求
MySQL 把 (UPPER(name)) 和 UPPER(name) 当作完全不同的东西处理:前者被识别为函数索引表达式,后者会被当作列名或语法错误。
- 正确写法:
CREATE INDEX idx_upper_name ON t1((UPPER(name)));—— 外层双括号不能省,里面函数括号也不能少 - 错误写法:
CREATE INDEX idx_upper_name ON t1(UPPER(name));(少一层括号,MySQL 解析失败) - 错误写法:
CREATE INDEX idx_upper_name ON t1((name));(单列加括号 ≠ 函数索引,会报错 “Functional index expression cannot be a column reference”) - 多表达式也得各自包住:
CREATE INDEX idx_date_sum ON orders((DATE(create_time)), (amount + tax));
哪些函数能用,取决于是否支持生成列
函数索引底层复用的是生成列(Generated Column)机制,所以只能用那些允许出现在 AS 子句里的确定性函数。
- 可用:
UPPER()、LOWER()、DATE()、YEAR()、SUBSTRING()、ABS()、COALESCE()、CAST() - 不可用:
NOW()、RAND()、UUID()、USER()、子查询、自定义函数、存储过程内函数 - JSON 类函数如
JSON_EXTRACT(data, '$.status')可用,但要注意返回类型必须明确(比如加CAST(... AS CHAR)避免隐式转换失败) - 算术表达式如
(a + b * 2)或(field1 - field2)允许,但别写太复杂,否则插入/更新时计算开销明显上升
函数索引和普通索引混用时的排序与方向控制
函数列可以和普通列共存于同一个复合索引里,但 ASC/DESC 必须显式声明,且只作用于它紧邻的表达式或列。
- 合法:
CREATE INDEX idx_on_expr_col ON t1((DATE(create_time)) DESC, status ASC); - 非法:
CREATE INDEX idx_bad ON t1((DATE(create_time) DESC), status);(DESC不能写在函数内部) - 注意:函数索引本身不支持
FULLTEXT或SPATIAL类型,也不能作为主键或外键引用目标 - 如果表已有大量数据,建函数索引会触发全表扫描计算虚拟列值,线上执行前务必在从库或低峰期测试耗时
验证函数索引是否真被用了
光看 SHOW INDEX FROM t1 不够,Expression 列显示非 NULL 才说明是函数索引;但真正起不起作用,得看执行计划。
- 用
EXPLAIN SELECT * FROM t1 WHERE UPPER(name) = 'TOM';——key字段必须显示你的函数索引名,type最好是ref或range - 如果
key是NULL,先检查 WHERE 条件是否和索引表达式**字面完全一致**(比如用了ucase(name)而索引是UPPER(name),就不匹配) - 函数索引不支持前缀匹配:
WHERE name LIKE 'abc%'无法用(UPPER(name))索引加速,因为大小写转换破坏了原始顺序 - 有个容易忽略的点:函数索引的虚拟列默认不可见,
SELECT *不会返回它,但你也不能在SELECT中直接引用这个“列名”,它只存在于索引结构里


















