sql JOIN的核心是明确连接条件与驱动表关系,关键在确定保留哪边数据、空值处理及重复控制;INNER、LEFT、RIGHT、FULL OUTER四种JOIN本质区别在于基准表选择与匹配强制性。

SQL JOIN 语句不是“拼表”那么简单,核心在于明确连接条件 + 理解驱动表与被驱动表的关系。写对 JOIN,关键不是记语法,而是想清楚:我要保留哪边的数据?匹配不上时要不要留空?重复数据怎么处理?
JOIN 类型选对,结果才不会“丢行”或“多行”
最常见的四种 JOIN,本质区别就两点:以哪张表为基准(保留所有行)、是否要求两边都必须匹配。
- INNER JOIN:只保留两表都能匹配上的行。没匹配的全过滤掉,最常用也最安全。
- LEFT JOIN:以左表为基准,左表每行都保留;右表没匹配上就填 NULL。
- RIGHT JOIN:以右表为基准,逻辑同 LEFT,只是左右互换(实际中基本不用,用 LEFT 替代更清晰)。
- FULL OUTER JOIN:两边都不舍弃,没匹配上的各自补 NULL(mysql 不原生支持,需用 LEFT + RIGHT + union 模拟)。
ON 条件写在哪,直接影响结果对不对
JOIN 后面的 ON 是连接逻辑的“开关”,它只管“怎么连”,不管“要不要留”。很多人把过滤条件错写在 ON 里,尤其在 LEFT JOIN 中,会导致语义错误。
- 正确做法:连接条件放 ON,比如 ON a.id = b.user_id;额外筛选放 WHERE,比如 WHERE b.status = ‘active’。
- 陷阱示例:LEFT JOIN 时若把 b.status = ‘active’ 写进 ON,会导致右表不满足该条件的行仍被保留(只是字段为 NULL),但本意可能是“只要左表有,且右表存在 active 记录才关联”——这时就得用子查询或 WHERE 控制。
别忽略 NULL,JOIN 后常要处理“空值干扰”
LEFT / RIGHT / FULL JOIN 必然引入 NULL,它们会影响 count、SUM、比较判断等操作。
- COUNT(*) 统计总行数,COUNT(b.id) 会自动忽略 b.id 为 NULL 的行(即只算有匹配的)。
- 判断是否存在关联时,别写 b.id != NULL,要用 b.id IS NOT NULL。
- 聚合前建议先用 COALESCE(b.amount, 0) 把 NULL 转成 0,避免 SUM 结果变 NULL。
性能提示:小表驱动大表,ON 字段建索引
数据库执行 JOIN 时,通常把左边当驱动表(尤其 MySQL 的 Nested Loop),所以:
- LEFT JOIN 尽量让左表小、右表大,并确保 ON 中的右表字段(如 b.user_id)有索引。
- 避免在 ON 或 WHERE 中对连接字段做函数操作,比如 ON YEAR(a.create_time) = YEAR(b.time),会无法走索引。
- 用 EXPLAIN 看执行计划,重点关注 type 是否为 ‘ref’ 或 ‘eq_ref’,key 是否命中索引。
基本上就这些。JOIN 不复杂但容易忽略细节,动手写之前,先问自己一句:我要的是交集?还是带补充信息的主表全量?答案清楚了,语法自然就顺了。