join条件中使用upper()、date()等函数会导致索引完全失效,优化器无法映射至b+树,被迫全表扫描或嵌套循环;coalesce()因确定性可优化,case when则因运行时求值被多数数据库禁止;类型不匹配叠加函数更致双重失效;修复需移除on中函数,改用生成列、统一输入规范或区间查询。

JOIN条件里写UPPER()、DATE()这类函数,索引直接作废
不是“可能不用”,而是优化器根本没法把函数结果映射到B+树索引上。它得对右表每一行先算一遍函数值,再逐个比对——等于主动放弃索引,强制走嵌套循环(Nested Loop)或全表扫描。
-
ON u.email = UPPER(o.email):o.email上的索引完全失效,EXPLAIN里type会变成ALL,rows接近o表总行数 -
ON a.order_date = DATE(b.ship_time):b.ship_time索引失效,且因日期截断丢失精度,还可能漏掉当天的后半段数据 -
ON t1.code = CONCAT('ORD-', t2.id):t2.id索引失效,同时CONCAT结果无法被左表索引覆盖,双向都崩
为什么COALESCE()可以,但CASE WHEN不能用在ON子句里
COALESCE(tpwp.org_code, lbtpwp.org_code)是确定性表达式,各数据库能将其折叠为一个可索引的连接键;而CASE WHEN在ON中属于运行时求值逻辑,与JOIN类型(LEFT/INNER)的编译期判定冲突。
- PostgreSQL、SQL Server、MySQL 8.0+ 直接报错:
ERROR 1064或Incorrect syntax near 'CASE' - 某些旧版MySQL 5.x“看似能跑”,实则优化器把CASE简化为恒真/恒假,导致全表扫描或意外丢行
- 即使表面成功,
rpwp.org_code = CASE WHEN tpwp.org_code IS NOT NULL THEN tpwp.org_code ELSE lbtpwp.org_code END也会让(org_code, wp_code)复合索引彻底失效
字段类型不一致 + 计算表达式 = 双重失效
当JOIN字段本身类型就不匹配(比如INT vs VARCHAR),再叠加上函数,问题会指数级放大。隐式转换会让两边都失去索引能力,还可能引入精度误差或空格干扰。
-
ON users.id = CAST(orders.user_id AS CHAR):orders.user_id索引失效,且CAST后字符串长度不可控,影响等值比较稳定性 -
ON a.id = b.a_id AND UPPER(a.name) = UPPER(b.name):a.name和b.name索引全废,且大小写规则(如utf8mb4_0900_as_cs)可能导致本该匹配的行被跳过 - 查HEX值确认不可见字符:
SELECT user_id, HEX(user_id) FROM orders WHERE user_id LIKE '%123%',看到20就是空格,09是制表符
真正可行的修复路径:把计算逻辑前置
别指望优化器帮你“猜意图”。函数必须从ON里彻底移除,换成可索引的原始字段或生成列。
- 业务层统一规范输入:邮箱存小写,查询也用小写,
ON u.email = o.email即可 - 用生成列+索引替代现场计算:
ALTER TABLE users ADD email_lower VARCHAR(255) AS (LOWER(email)) STORED,再对email_lower建索引 - 如果必须兼容混合大小写,改在应用层标准化,而不是在SQL里调UPPER()
- 时间范围类计算,改写为区间:
WHERE o.create_time >= '2023-01-01' AND o.create_time ,而非<code>YEAR(o.create_time) = 2023
UPPER(),只要users.id是BIGINT而orders.user_id是VARCHAR,索引照样不生效。











