on子句必须紧随对应join,不可后置;混用left/inner join时顺序影响语义;复合关联应拆分;on仅用于关联字段,业务过滤须移至where或子查询。

ON条件必须紧贴对应JOIN,不能堆在最后统一写
SQL语法根本不允许把所有JOIN列完再统一写ON。像 FROM t1 JOIN t2 JOIN t3 ON t1.id = t2.id AND t2.cid = t3.id 这种写法在MySQL、PostgreSQL、SQL Server里都会报错——解析器无法判断第二个ON到底属于哪个JOIN。
每个JOIN后面必须立刻跟它的ON子句,这是语法硬性要求,不是风格建议。
- 正确写法:
FROM t1 JOIN t2 ON t1.id = t2.t1_id JOIN t3 ON t2.id = t3.t2_id - 错误写法:
FROM t1 JOIN t2 JOIN t3 ON ...(语法错误) - 哪怕只用
JOIN不写类型,默认也是INNER JOIN,但ON仍需紧随其后
LEFT JOIN和INNER JOIN混用时,ON顺序影响语义,不只影响可读性
比如要查「用户 + 他们的活跃订单 + 订单对应的商品」,有人会这么写:
SELECT u.name, o.order_no, p.title FROM users u LEFT JOIN orders o ON u.id = o.user_id INNER JOIN products p ON o.product_id = p.id;
这实际等价于先做LEFT JOIN生成中间结果,再对这个中间结果做INNER JOIN——但中间结果里o字段可能为NULL,导致o.product_id = p.id永远不成立,最终p.title全为NULL,且u中没订单的用户也被整个剔除(因为INNER JOIN失败)。
- 真正想保留所有用户?得把
products也用LEFT JOIN:LEFT JOIN products p ON o.product_id = p.id - 想只取有活跃订单的用户?直接把第一个
LEFT JOIN换成INNER JOIN更清晰 - 顺序一旦混用
LEFT和INNER,执行逻辑就变成“左表→中间结果→再连接”,不是并列关系
复合关联条件(跨三张表)别硬塞进ON,该拆就拆
看到类似 ON t1.a = t2.a AND t2.b = t3.b 的写法要警觉:这说明t2.b本不该和t3直接挂钩,而是t1和t3之间缺了合理外键,或者业务模型本身存在歧义。
强行写会导致:
- 优化器难以估算行数,
EXPLAIN里filtered值可能暴跌 - 维护者看不懂谁该为
t2.b负责,改t3结构时容易漏掉这里 - MySQL 8.0+虽支持,但PostgreSQL对这种非直接关联的
ON推导更保守
更稳妥的做法是用子查询或CTE提前规整t2逻辑,例如:
FROM users u LEFT JOIN ( SELECT o.*, p.category FROM orders o LEFT JOIN products p ON o.product_id = p.id ) op ON u.id = op.user_id
ON里只放关联字段,业务过滤一律挪到WHERE或子查询
ON的唯一职责是定义“哪两行能连上”。往里面塞t2.status = 'active'看似省事,实则扭曲语义:
- 在
LEFT JOIN中,它会让不满足的右表行变NULL,而不是被跳过——你拿到的是“用户 + (活跃订单或NULL)”,不是“用户 + 活跃订单” - 在
INNER JOIN中虽结果等效,但可读性差,后续加WHERE时容易误判条件归属 - 如果
t2.status没索引,ON里用它可能导致连接走不了索引,而WHERE阶段还能靠其他条件触发索引下推
真要筛活跃订单?要么用INNER JOIN orders o ON ... AND o.status = 'active'(明确表达“只要活跃的”),要么用WHERE o.status = 'active'(前提是已用INNER JOIN)。别在LEFT JOIN的ON里埋业务逻辑。











