on子句中使用like '%xxx%'会导致全表扫描且结果不可控,因前导通配符使索引失效,left join下更恶化为o(n×m)复杂度,应改用精确join+where过滤或应用层处理。

直接在 JOIN 的 ON 子句里写 t1.name LIKE CONCAT('%', t2.keyword, '%'),90% 的情况会慢到不可用,且结果不可控——不是语法错,而是执行逻辑和数据质量双重陷阱。
为什么 ON 里用 LIKE '%xxx%' 会全表扫描
数据库优化器无法对含前导通配符的 LIKE 表达式使用 B+ 树索引。哪怕 t1.name 上建了索引,LIKE '%john%' 仍强制对每行计算匹配,等价于逐行 SUBSTRING 扫描。
- MySQL/PostgreSQL/SQL Server 全部适用:只要
LIKE模式以%开头,索引失效 -
LEFT JOIN场景更危险:驱动表(左表)每行都要遍历右表全量做模糊判断,复杂度是 O(n×m) - 真实错误现象:
EXPLAIN显示type: ALL、rows等于右表总行数,key: NULL
LEFT JOIN 中 t2.keyword 为空或含特殊字符怎么办
CONCAT('%', NULL, '%') 返回 NULL,导致整个 ON 条件为 UNKNOWN,该行被当作不匹配丢弃——即使你本意是“空 keyword 视为通配”。
- 必须显式过滤:
ON ... AND t2.keyword IS NOT NULL AND t2.keyword != '' - 特殊字符如
%、_、[在LIKE中有语义,需转义:LIKE CONCAT('%', REPLACE(REPLACE(t2.keyword, '\', '\\'), '%', '%'), '%') ESCAPE '\' - 字段未加表别名时易歧义:若
name和keyword在两表都存在,MySQL 报错Column 'name' in on clause is ambiguous
替代方案:把模糊逻辑从 ON 挪到 WHERE 或子查询
先用精确条件缩小右表规模,再在内存或应用层做字符串判断,比硬扛 SQL 模糊 JOIN 快一个数量级。
- 推荐顺序:
JOIN基于city_id、category_code等高选择率字段 →WHERE中用LIKE过滤结果集 - 或改用子查询预过滤右表:
(SELECT * FROM t2 WHERE keyword IS NOT NULL AND LENGTH(keyword) >= 2) AS t2_filtered - Python 应用层处理示例:
df1.merge(df2, how='left', left_on='city_id', right_on='city_id').query("name.str.contains(keyword, na=False)")
真要 SQL 内完成,优先用前缀匹配 + 函数索引
只有 LIKE 'prefix%' 能走普通索引;若业务允许,把模糊需求收敛为“开头一致”,是最简单有效的解法。
- MySQL 示例:
ALTER TABLE users ADD INDEX idx_name_prefix (name(10)),配合ON u.name LIKE CONCAT(t2.prefix, '%') - PostgreSQL 可建函数索引:
CREATE INDEX idx_name_lower ON t1 ((lower(name))),再用ON lower(t1.name) LIKE lower(CONCAT(t2.keyword, '%')) - 注意:
CONCAT(t2.keyword, '%')中t2.keyword必须非空且不含通配符,否则结果失真
最常被忽略的一点:模糊 JOIN 的语义本身是脆弱的——t2.keyword 若来自用户输入或爬虫脏数据,一次 % 就可能让整条 SQL 返回数千行噪音。宁可多一道应用层校验,也不要依赖数据库硬算模糊性。










