mysql 8.0 支持函数索引,但仅限确定性表达式且表必须为 innodb 引擎;语法须为 create index idx on tbl ((expr)),仅支持白名单内置函数(如 upper、json_extract),不支持自定义函数或非确定性函数。

MySQL 8.0 支持函数索引(Functional Indexes),但仅限于 CREATE INDEX 语句中使用确定性表达式,且表必须是 InnoDB 引擎 —— 这不是“任意函数都能建索引”,而是有严格限制的。
FUNCTIONAL INDEXES 只支持表达式,不支持自定义函数
很多人误以为能像 PostgreSQL 那样直接对 UPPER(name) 或 JSON_EXTRACT(data, '$.id') 建索引,实际上 MySQL 8.0 确实支持,但前提是表达式必须是「确定性」且被明确列入白名单。例如:
-
UPPER(col)、LOWER(col)✅ 允许 -
JSON_EXTRACT(col, '$.field')✅ 允许(需确保路径合法、返回标量) -
CAST(col AS UNSIGNED)✅ 允许 -
my_udf(col)❌ 不允许 —— 自定义函数一律禁止 -
NOW()、RAND()❌ 非确定性,直接报错ERROR 3752 (HY000): Functional index expression must be deterministic
建函数索引的语法和必要条件
必须显式指定列名(哪怕只是占位),并用括号包裹表达式;索引名不能省略;表必须为 InnoDB;字段本身不需要单独存在 —— 表达式可基于不存在的虚拟列,但 MySQL 要求该表达式所有引用列都真实存在。
正确写法示例:
CREATE INDEX idx_upper_name ON users ((UPPER(name)));
注意:((UPPER(name))) 的双括号是语法必需 —— 外层括号表示“函数表达式”,内层是函数调用本身。漏掉任一层都会报错 ERROR 1064。
- 必须用
((expr))格式,不能写成(UPPER(name))或UPPER(name) - 索引名如
idx_upper_name必须唯一,且不能与已有列名冲突 - 若表是
MyISAM或其他引擎,会提示ERROR 1286: Unknown storage engine 'InnoDB'(即使你没写引擎,也要确认实际引擎) - 表达式结果类型必须可索引:不能是
TEXT、BLOB、未指定长度的VARCHAR(如VARCHAR无长度)
函数索引生效的关键:查询条件必须完全匹配表达式
索引不会自动“适配变形”。比如建了 ((UPPER(name))),只有 WHERE UPPER(name) = 'JOHN' 才能走索引;WHERE name = 'john' 或 WHERE LOWER(name) = 'john' 都不命中。
- 大小写敏感场景下,
WHERE name LIKE 'joh%'无法利用UPPER(name)索引 - 如果想同时支持大小写模糊查和前缀查,得另建普通索引或组合索引
-
JSON_EXTRACT索引只在WHERE JSON_EXTRACT(meta, '$.status') = 'active'时有效,换成->'$.status'操作符(等价但语法不同)也可能不命中 —— 实测建议统一用JSON_EXTRACT - 执行
EXPLAIN时,key字段显示索引名,possible_keys包含它,才说明真用了
常见报错与绕过方式
最常遇到的是 ERROR 3752 和 ERROR 1064,本质是表达式不合规或语法不对。临时解决思路有限,没有“强制创建”选项。
- 报错
ERROR 3752: Functional index expression must be deterministic→ 检查是否用了SUBSTRING_INDEX(允许)、REPLACE(允许),但避免嵌套调用不确定函数(如带@var变量) - 报错
ERROR 1064: You have an error in your SQL syntax→ 90% 是括号数量不对,或漏了索引名,或用了反引号包错位置(如INDEX `idx` ON t ((UPPER(`name`)))合法,但INDEX idx ON t (UPPER(name))非法) - 想对
DATE(created_at)建索引?不行 ——DATE()在 MySQL 8.0.13+ 才被加入确定性函数列表,旧版本会拒绝。升级前先查SELECT VERSION(); - 实在需要复杂逻辑,只能退回到生成列(
GENERATED COLUMN)+ 普通索引:先ALTER TABLE t ADD COLUMN name_upper VARCHAR(64) STORED AS (UPPER(name)),再CREATE INDEX idx_name_upper ON t (name_upper)
函数索引看着灵活,实际约束多、匹配严、调试难——真正上线前,务必用 EXPLAIN FORMAT=TRADITIONAL 对每条目标查询做验证,别信“建了就有效”。











