on子句中写常量(如1=1)或单边条件会导致笛卡尔积、索引失效和语义错误,必须使用关联两表字段的表达式来定义行间映射关系。

ON 子句里写常量(比如 ON 1=1 或 ON t1.id = -1)不是语法错误,但几乎总是逻辑错误——它会让连接失去“按业务关系匹配”的意义,变成无差别拉取或意外爆炸。
ON 中写 1=1 或 t2.id = -1 会怎样
这类条件不依赖任何表字段的值,数据库无法用它做有效筛选或索引下推。实际效果取决于具体写法:
-
LEFT JOIN t2 ON 1=1→ 左表每行都跟右表所有行配对,等价于CROSS JOIN,结果行数 = 左表行数 × 右表行数 -
LEFT JOIN t2 ON t2.id = -1→ 只有右表中id = -1的那一行(如果存在)会被连到左表每一行上;左表每行都会多出一重复副本 -
INNER JOIN t2 ON t2.status = 'active'→ 如果没加其他关联条件,这也不是“按用户连订单”,而是把所有status = 'active'的记录全拉出来跟左表硬拼,照样笛卡尔积
为什么 ON 必须是“两个表之间的关联表达式”
ON 的本质是定义“哪几行能组成一条逻辑记录”。它需要同时引用至少两个表的字段(或其表达式),让数据库能据此建立行与行之间的映射关系。一旦只写常量或单边字段:
- 优化器无法估算连接基数,大概率放弃使用索引,走嵌套循环甚至临时表
- EXPLAIN 里会出现
type: ALL和rows异常高,Extra字段常带Using join buffer - 语义上丢失了“关联”意图:你本想查“用户和他下的订单”,结果查出了“每个用户和所有活跃订单”
哪些看似合理、实则危险的 ON 写法
这些写法容易被当成“过滤”,但它们出现在 ON 里会直接破坏连接逻辑:
-
ON t1.id = t2.t1_id AND t2.deleted = 0✅ 安全——t2.deleted是右表字段,且参与匹配判断 -
ON t1.id = t2.t1_id AND 'active' = 'active'❌ 无意义常量,等同于ON t1.id = t2.t1_id,纯属干扰可读性 -
ON t1.created_at >= '2025-01-01'❌ 单边条件,没提t2,数据库只能全扫t1再暴力匹配 -
ON COALESCE(t2.type, 'unknown') = 'normal'⚠️ 表达式本身合法,但若t2.type无索引,或该列 NULL 值极多,会导致右表几乎全扫
真正该在 ON 里出现的内容长什么样
一句话判断标准:去掉这个条件,连接结果集的结构(即哪些左表行能连上哪些右表行)是否发生改变? 如果不变,那它就不该在 ON 里。
- 必须含两边字段:如
t1.user_id = t2.owner_id、t1.code = UPPER(t2.ref_code)(注意后者可能使索引失效) - 允许简单函数,但需确保可利用索引:如
DATE(t2.occurred_at) = CURDATE()❌,应改为t2.occurred_at >= CURDATE() AND t2.occurred_at ✅ - 多条件用
AND组合,避免OR(易导致索引失效或执行计划退化)
最常被忽略的一点:ON 不是 WHERE 的前置替代品。它不负责“筛数据”,只负责“建关系”。哪怕你只想连右表中 status = 'paid' 的记录,也得先确保 t1.id = t2.t1_id 这个骨架存在,再把 t2.status = 'paid' 当作连接约束补上去——否则,你就不是在查“已支付的订单”,而是在查“所有用户 + 所有已支付订单的任意组合”。










