结论:对索引列使用函数(如date()、year()、lower()、left())会导致mysql无法利用b+树索引的有序性,只能全表扫描;因其破坏原始值匹配,优化器无法定位索引节点,典型表现为explain中type=all、key=null。

直接说结论:只要在 WHERE 子句里对索引列用了函数(比如 DATE()、YEAR()、LOWER()、LEFT()),MySQL 就没法走索引,只能全表扫描——这不是 Bug,是 B+ 树索引的底层逻辑决定的。
为什么函数会让索引失效
索引是按字段原始值排序构建的 B+ 树,而函数会改变字段值的形态和顺序。比如 create_time 索引里存的是 '2023-10-01 14:22:33' 这种完整时间戳,但 DATE(create_time) 把它变成 '2023-10-01',MySQL 没法从原始索引树里“跳”到这个新值上,只能逐行计算再比对。
常见错误现象:
-
EXPLAIN显示type=ALL、key=NULL - 500 万行表查询耗时从 8ms 暴涨到 12s
- 数据库 CPU 突然拉满,慢查询日志里反复出现带函数的 SQL
优先用范围查询替代函数
这是最简单、兼容性最好、性能提升最直接的方式,适用于日期、数值等可拆解场景。
不要写:
SELECT * FROM orders WHERE DATE(create_time) = '2023-10-01';
改写为:
SELECT * FROM orders WHERE create_time >= '2023-10-01 00:00:00' AND create_time <p>其他常见替换方式:</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill2334" title="MySQL"><img src="https://img.php.cn/upload/skill/000/000/081/178900927846657.jpg" alt="MySQL" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill2334" title="MySQL" class="overflowclass">MySQL</a> <p class="overflowclass">编写正确的MySQL查询,避免字符集、索引和锁方面的常见陷阱。</p> </div> <a rel="nofollow" href="/xiazai/skill2334" title="MySQL" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>
-
YEAR(create_time) = 2023→create_time >= '2023-01-01' AND create_time -
LEFT(name, 3) = 'tom'→name LIKE 'tom%'(前提是能接受前缀匹配) -
price * 1.1 > 100→price > 100 / 1.1
注意:LIKE 只有右模糊('tom%')能走索引,'%tom' 或 '%tom%' 依然失效。
MySQL 8.0.13+ 可用函数索引,但别乱建
如果你确实绕不开函数(比如要查大小写不敏感的用户名),且 MySQL 版本 ≥ 8.0.13,可以直接建函数索引:
CREATE INDEX idx_lower_name ON users ((LOWER(name)));
关键点:
- 括号不能少:
((LOWER(name)))是语法要求,不是笔误 - 函数索引只对精确匹配有效,
WHERE LOWER(name) = 'alice'能命中,WHERE LOWER(name) LIKE 'ali%'不能 - 函数索引会额外占用存储空间,且写入时需计算虚拟列值,高并发写入场景要评估开销
- 低版本(ALTER TABLE users ADD COLUMN name_lower VARCHAR(64) AS (LOWER(name)) STORED;,再给该列建普通索引
最容易被忽略的坑:隐式类型转换 + 函数叠加
线上最隐蔽的失效组合:字段是 VARCHAR,你传了数字参数,又套了函数——双重打击。
比如:
SELECT * FROM users WHERE LOWER(phone) = 13800138000;
这里既触发了隐式转换(phone 被转成数字),又用了 LOWER(),索引彻底失效。正确写法必须同时满足:
- 类型一致:
phone是字符串,右边就得加引号 - 避免函数:
WHERE phone = '13800138000'(如果业务允许) - 或函数索引:
CREATE INDEX idx_lower_phone ON users ((LOWER(phone)));+WHERE LOWER(phone) = '13800138000'
真正麻烦的从来不是单个函数,而是开发时没意识到——一个看似无害的类型不匹配,加上一个理所当然的函数调用,就让之前所有索引优化白费。










