where中直接调用mysql函数会导致索引失效,引发全表扫描;应避免date()、upper()等函数,改用范围查询如create_time >= ‘2024-01-01 00:00:00’ and create_time
WHERE 中直接调用 mysql 函数会拖慢查询速度
MySQL 在
WHERE子句里对字段使用函数(比如date(created_at)、UPPER(name)、YEAR(order_date))时,通常会导致该字段无法走索引,哪怕它上面建了索引。这是因为函数改变了原始值的存储形态,优化器无法用 B+ 树索引做快速定位。常见现象:执行
EXPLAIN后看到type=ALL或key=NULL,说明全表扫描了。
- 避免写
WHERE DATE(create_time) = '2024-01-01',改用范围查询:WHERE create_time >= '2024-01-01 00:00:00' AND create_time- 避免
WHERE UPPER(email) = 'ADMIN@EXAMPLE.COM',改在写入时统一转小写并建索引,查询用WHERE email = 'admin@example.com'- 日期分组场景下,别在
WHERE里用WEEK()或MONTH(),优先考虑生成计算列或冗余字段(如year_month char(7))并索引它函数索引(MySQL 8.0.13+)能解决部分问题
MySQL 8.0.13 起支持函数索引(Functional Key),允许对表达式建索引,比如
UPPER(name)或DATE(updated_at)。但要注意它不是“万能加速器”,而是有明确限制的补救手段。使用前确认:
select VERSION();返回 ≥ 8.0.13;且表引擎是 InnoDB。
- 建索引示例:
CREATE INDEX idx_upper_name ON users ((UPPER(name)));—— 注意双括号语法- 查询必须完全匹配该表达式才能命中:
WHERE UPPER(name) = 'JOHN'可走索引;WHERE name LIKE 'j%'不行- 函数索引不支持全文索引、空间索引,也不适用于
JSON_EXTRACT()等返回非标量结果的函数WHERE 里用函数 ≠ 一定不能优化,关键看是否可下推
有些函数其实是“索引友好型”的,比如
CAST()、CONVERT()在类型兼容时可能被优化器下推,而SUBSTRING()、REPLACE()基本无法下推。是否能走索引,最终取决于优化器能否将条件重写为索引可比较的形式。
WHERE CAST(id AS CHAR) = '123':如果id是整型主键,优化器大概率会转成WHERE id = 123,仍可用索引WHERE SUBSTRING(phone, 1, 3) = '138':无法利用phone字段上的普通索引,除非建前缀索引(如INDEX idx_phone_prefix (phone(3)))- 用
EXPLAIN format=TREE(MySQL 8.0+)可看清优化器是否做了“Condition pushdown”替代方案比硬扛函数更可靠
比起在
WHERE里硬套函数,更可持续的做法是调整数据模型或查询逻辑。函数只是临时绕开设计缺陷的胶带,不是长期解法。
- 把常用计算结果存为生成列(
GENERATED column),并为其加索引。例如:date_only DATE AS (DATE(created_at)) STORED,再CREATE INDEX idx_date_only ON t1(date_only)- 业务上需要频繁按小时统计?考虑在写入时就拆出
hour_key CHAR(13)(如'2024-01-01-14'),避免每次查都算HOUR(created_at)- 模糊前缀匹配(如
LIKE 'abc%')可以走索引;但LIKE '%abc'或LIKE '%abc%'不行,此时应评估是否引入全文索引或外部搜索引擎真正卡住性能的,往往不是函数本身,而是函数掩盖了索引设计缺失或数据分布不合理。先看
EXPLAIN,再想函数,最后才考虑加索引——顺序错了,优化就容易白忙。
