JOIN条件中使用UPPER()、DATE()、CONCAT()等函数会导致索引彻底失效,优化器无法利用B+树索引定位数据,被迫执行全表扫描;正确做法是前置标准化(如业务层统一存储规范值或使用生成列+索引),而非依赖函数索引或现场计算。

JOIN列上用UPPER()、DATE()、CONCAT()这类函数,索引直接失效,优化器只能全表扫描——这不是“可能慢”,而是B+树索引根本无法参与定位。
ON子句里调用函数,索引就彻底作废
只要ON条件中任意一侧对字段做了计算,比如ON u.email = UPPER(o.email)或ON a.order_code = CONCAT('ORD-', b.id),该侧字段的索引就完全没机会被使用。优化器必须为每一行实时算出函数结果,再逐行比对,等于主动放弃索引路径。
- EXPLAIN里看到被驱动表的
type=ALL、rows接近表总行数,就是铁证 -
Extra出现Using join buffer (Block Nested Loop),说明已退化为嵌套循环 - 哪怕
o.email有唯一索引、b.id是主键,也毫无意义
函数索引不是万能解药
MySQL 8.0+虽支持函数索引,但实际在JOIN中很难生效:建了INDEX idx_upper_email (UPPER(email)),写成ON UPPER(u.email) = UPPER(o.email),通常只有一侧能命中;另一侧仍要逐行计算,优化器大概率弃用整个索引路径。
- 函数索引要求表达式完全一致、字段类型严格匹配、无隐式转换
-
CONCAT()、SUBSTR()等非标函数在索引定义和JOIN写法间稍有偏差,就失效 - LEFT JOIN右表字段加函数更危险:不仅自身索引失效,还会强制优化器放弃哈希连接,退化为N×全表扫描
字符集或COLLATION不一致也会触发同样现象
即使没写函数,ON t1.name = t2.name也可能全表扫描——如果两字段collation_name不同(比如utf8mb4_0900_as_cs vs utf8mb4_unicode_ci),优化器无法保证B+树比较语义正确,会主动弃用索引。
- 查确认:用
SELECT table_name, column_name, collation_name FROM information_schema.COLUMNS WHERE table_name IN ('t1','t2') AND column_name = 'name' -
ON里硬加COLLATE没用,运行时转换无法下推,索引照样不走 - 必须用
ALTER TABLE ... MODIFY同时指定CHARACTER SET和COLLATE,改字段定义本身
真正稳定的解法从来不是“怎么写函数”,而是把计算逻辑从SQL层移走:业务层统一存小写邮箱、预生成'ORD-12345'订单号,或者用生成列+普通索引替代。现场拼接和实时转换,本质是把应用层职责强塞给数据库,一到百万级数据,问题就藏不住了。










