每个JOIN必须写ON,否则导致笛卡尔积;LEFT JOIN右表条件须放ON而非WHERE;子查询作表需ON关联;多表连接须每对都有业务ON;一对多直接JOIN会隐性膨胀。

每个JOIN后面必须写ON,不写就是笛卡尔积
显式JOIN(如LEFT JOIN、INNER JOIN)不会自动猜字段关系。漏掉ON子句,MySQL 8.0+ 直接报ERROR 1064;旧版或某些ORM可能静默退化为CROSS JOIN,结果就是纯笛卡尔积。
-
SELECT * FROM orders LEFT JOIN customers;→ 错误,等效于CROSS JOIN -
SELECT * FROM orders LEFT JOIN customers ON orders.customer_id = customers.id;→ 正确 -
USING (customer_id)可以替代ON,但要求两表字段名完全一致、类型兼容,不是“省事借口”
LEFT JOIN的右表条件必须放ON里,不能放WHERE
WHERE在连接完成后才执行,而ON控制连接过程本身。把右表过滤条件塞进WHERE,会把本该保留的左表空匹配行也干掉,实际效果等于INNER JOIN,还多跑一遍全量配对。
- 错误:
LEFT JOIN customers c ON o.customer_id = c.id WHERE c.status = 'active'→ 所有无客户匹配或状态非 active 的订单全丢 - 正确:
LEFT JOIN customers c ON o.customer_id = c.id AND c.status = 'active'→ 左表行数不变,只拉活跃客户 - 若业务要求“所有订单 + VIP用户信息”,
WHERE里绝不能出现c.level这类右表字段
子查询当表用时,照样要ON关联,且得控粒度
子查询出现在FROM或JOIN右侧,就变成一张临时表,和外层表之间仍需真实业务意义的ON条件。更危险的是:没聚合、没去重,就把明细原样摊开再连。
- 危险:
LEFT JOIN (SELECT order_id, SUM(amount) FROM payments GROUP BY order_id) b ON 1=1→ON 1=1等同于没限制 - 安全:
LEFT JOIN (SELECT order_id, SUM(amount) AS total FROM payments GROUP BY order_id) b ON a.id = b.order_id - 一对多场景下,优先在子查询里先聚合;要最新一条而非全部明细,用
ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY created_at DESC)筛
三张及以上表连接,检查每对相邻表是否都有业务ON条件
不能只确保首尾两张表有关联,中间每一对都得有真实业务逻辑的ON。跳过中间实体强行连接,数据库只能暴力匹配,中间表形同虚设。
- 错误链路:
orders JOIN products ON orders.id = products.id→ 没这业务逻辑,order_items被绕过 - 正确链路:
orders JOIN order_items ON orders.id = order_items.order_id JOIN products ON order_items.product_id = products.id - 验证方法:对每个
JOIN后的中间结果加LIMIT 5看行数是否合理;某步后突增数十倍,大概率断链了
真正容易被忽略的,是“一对多表同时关联主表”引发的隐性膨胀——比如主表1行,明细A表3行,明细B表5行,直接LEFT JOIN两者,结果就是15行。这不是语法错误,但业务上往往不需要这种组合,得靠子查询先聚合或改用EXISTS来规避。

















