SQL表连接终于讲明白了:INNER JOIN、LEFT JOIN、RIGHT JOIN 一次学透
很多人学 SQL,卡得最久的不是 SELECT、WHERE,而是表连接(JOIN)。这篇就不绕弯,直接把 SQL 表连接讲到能上手。
一、为什么一定要学会表连接?
真实业务里,数据通常不会全塞在一张表里。比如电商系统里,用户信息在 users 表,订单信息在 orders 表。你想查“每个订单是谁下的”“哪些用户没下单”,就必须把多张表关联起来查。
JOIN 的本质:把不同表中有关联的数据,按条件拼接起来。
二、先记住 JOIN 的核心结构
SELECT 列名 FROM 表A JOIN 表B ON 表A.关联字段 = 表B.关联字段;
比如:
SELECT users.user_name, orders.order_id, orders.amount FROM users INNER JOIN orders ON users.user_id = orders.user_id;
ON 说白了就是:两张表到底凭什么配对。这个条件写错,后面全白搭。
三、INNER JOIN:只要两边都能对上
INNER JOIN 只返回两张表中能够匹配成功的数据。
SELECT users.user_name, orders.order_id, orders.amount FROM users INNER JOIN orders ON users.user_id = orders.user_id;
如果用户表里有小王、小李、小张,订单表里只有小王和小李的订单,那么结果中不会出现小张。
一句人话总结: 能配上的留下,配不上的滚。
四、LEFT JOIN:左边全保留,右边能接上就接
LEFT JOIN 的逻辑是:以左表为主,左表的所有记录都保留,右表匹配不上就补 NULL。
SELECT users.user_name, orders.order_id, orders.amount FROM users LEFT JOIN orders ON users.user_id = orders.user_id;
这时即使小张没有下单,也会出现在结果中,只不过订单相关字段是 NULL。
查“没有下单的用户”时,这是高频写法:
SELECT users.user_id, users.user_name FROM users LEFT JOIN orders ON users.user_id = orders.user_id WHERE orders.order_id IS NULL;
五、RIGHT JOIN:能不用就少用
RIGHT JOIN 和 LEFT JOIN 一样,只是把“主表”换到了右边。大多数团队更习惯把主表写在左侧,所以开发里通常更推荐统一使用 LEFT JOIN,少用 RIGHT JOIN,省得自己给自己上强度。
六、FULL OUTER JOIN:两边都要,谁落单都留下
FULL OUTER JOIN 会保留左右两边全部记录,匹配得上就拼起来,匹配不上就补 NULL。这个在数据核对、对账时很好用。
不过注意:MySQL 不直接支持 FULL OUTER JOIN,很多时候需要用 UNION 模拟。所以如果你是 MySQL 初学者,先把 INNER JOIN 和 LEFT JOIN 吃透,已经够用了。
七、记 JOIN,不如记这个问题
别死背定义,直接问自己一句:
我到底想保留哪张表的全部数据?
这个问题一问,JOIN 类型通常就出来了:
- INNER JOIN:只要交集
- LEFT JOIN:左边全保留
- RIGHT JOIN:右边全保留
- FULL OUTER JOIN:两边都保留
八、初学者最容易踩的 4 个坑
1)忘了写 ON 条件
SELECT * FROM users JOIN orders;
这要么直接报错,要么变成笛卡尔积。别查数据,查着查着就把数据库现场炸成烟花。
2)关联字段不唯一,结果重复
一对多、多对多关系本来就会让结果行数膨胀。写 JOIN 前先搞清楚关系:一对一?一对多?多对多?否则你会以为是 SQL 抽风,其实是你没想明白。
3)LEFT JOIN 后又在 WHERE 里把右表卡死
SELECT users.user_name, orders.order_id FROM users LEFT JOIN orders ON users.user_id = orders.user_id WHERE orders.amount > 100;
这会把右表为空的记录过滤掉,效果更像 INNER JOIN。更稳的写法是把过滤条件写到 ON 里:
SELECT users.user_name, orders.order_id FROM users LEFT JOIN orders ON users.user_id = orders.user_id AND orders.amount > 100;
4)字段重名不加表前缀
如果两张表里都有 user_id,那你裸写字段名,数据库十有八九会回你一句:字段不明确。老老实实写全,或者直接上表别名:
SELECT u.user_name, o.order_id, o.amount FROM users u LEFT JOIN orders o ON u.user_id = o.user_id;
九、建议直接收藏的 4 个实战模板
1)查订单和对应用户名
SELECT u.user_name, o.order_id, o.amount FROM users u INNER JOIN orders o ON u.user_id = o.user_id;
2)查所有用户及其订单,没有订单也显示
SELECT u.user_name, o.order_id, o.amount FROM users u LEFT JOIN orders o ON u.user_id = o.user_id;
3)查没有下单的用户
SELECT u.user_id, u.user_name FROM users u LEFT JOIN orders o ON u.user_id = o.user_id WHERE o.order_id IS NULL;
4)统计每个用户的订单数
SELECT u.user_name, COUNT(o.order_id) AS order_count FROM users u LEFT JOIN orders o ON u.user_id = o.user_id GROUP BY u.user_name;
十、最后总结
- INNER JOIN: 只要匹配成功的数据
- LEFT JOIN: 左表全保留,右表匹配不上就 NULL
- 查“没有”: 优先想 LEFT JOIN + IS NULL
- LEFT JOIN 右表过滤: 很多时候要写在 ON 里,不要乱塞 WHERE
SQL 表连接不难,它只是经常被讲得很绕。一旦你抓住“保留谁”这个核心,JOIN 基本就顺了。