LEFT JOIN
上一课的 JOIN 只留下两边配得上的行。可很多问题偏偏要问配不上的那些:哪些客户一单都没下过?哪件商品从来没人买?这一课讲 LEFT JOIN,它把左边的表一行不少地留下来。
配不上的行没了
先用上一课的写法,把客户和订单接起来:
1SELECT c.id, c.name, o.id AS order_id2FROM customers AS c3JOIN orders AS o ON o.customer_id = c.id4ORDER BY c.id, o.id;
| id | name | order_id |
|---|---|---|
| 1 | 林晓 | 1 |
| 1 | 林晓 | 3 |
| 1 | 林晓 | 9 |
| 2 | 王磊 | 2 |
| 2 | 王磊 | 7 |
| 3 | 陈静 | 6 |
| 4 | 张伟 | 4 |
| 4 | 张伟 | 12 |
| 5 | 刘洋 | 5 |
| 5 | 刘洋 | 11 |
| 7 | 孙强 | 8 |
| 8 | 周婷 | 10 |
结果有 12 行,一个订单一行。客户一共 10 个,可 6、9、10 号客户不在里面:他们没下过单,在 orders 里找不到配对的行,JOIN 就把他们丢掉了。
LEFT JOIN
把 JOIN 换成 LEFT JOIN,别的都不改:
1SELECT c.id, c.name, o.id AS order_id2FROM customers AS c3LEFT JOIN orders AS o ON o.customer_id = c.id4ORDER BY c.id, o.id;
| id | name | order_id |
|---|---|---|
| 1 | 林晓 | 1 |
| 1 | 林晓 | 3 |
| 1 | 林晓 | 9 |
| 2 | 王磊 | 2 |
| 2 | 王磊 | 7 |
| 3 | 陈静 | 6 |
| 4 | 张伟 | 4 |
| 4 | 张伟 | 12 |
| 5 | 刘洋 | 5 |
| 5 | 刘洋 | 11 |
| 6 | 赵敏 | NULL |
| 7 | 孙强 | 8 |
| 8 | 周婷 | 10 |
| 9 | 吴昊 | NULL |
| 10 | 郑雪 | NULL |
现在是 15 行。6、9、10 号客户回来了,他们的 order_id 是 NULL。
LEFT JOIN 的意思是:写在 FROM 后面的那张表(左边的表)每一行都要留下。配得上的,和上一课一样接上右边的行;配不上的,也留下一行,右边的列全部填 NULL。
| 左边这一行 | JOIN | LEFT JOIN |
|---|---|---|
| 配上 1 行 | 1 行 | 1 行 |
| 配上 3 行 | 3 行 | 3 行 |
| 一行也配不上 | 没了 | 1 行,右边全是 NULL |
单步看补 NULL
点上面那段查询的单步,看 JOIN 那一步:每个客户去 orders 里找 customer_id 等于自己 id 的订单。1 号林晓找到 3 个,就变成 3 行;走到 6 号赵敏时一个也没找到,她还是留下了一行,右边的格子全是 NULL。
右边的列不止 id。选上订单的所有列,看得更清楚(o.* 就是 o 这张表的所有列):
1SELECT c.name, o.*2FROM customers AS c3LEFT JOIN orders AS o ON o.customer_id = c.id4WHERE c.id >= 85ORDER BY c.id;
| name | id | customer_id | order_date | status |
|---|---|---|---|---|
| 周婷 | 10 | 8 | 2025-10-20 | 已发货 |
| 吴昊 | NULL | NULL | NULL | NULL |
| 郑雪 | NULL | NULL | NULL | NULL |
周婷有一个订单,四列都有值;吴昊和郑雪没有订单,orders 的四列都是 NULL。
没下过单的客户
既然配不上的行右边是 NULL,只留这些行,就是「没下过单的客户」:
1SELECT c.id, c.name2FROM customers AS c3LEFT JOIN orders AS o ON o.customer_id = c.id4WHERE o.id IS NULL5ORDER BY c.id;
| id | name |
|---|---|
| 6 | 赵敏 |
| 9 | 吴昊 |
| 10 | 郑雪 |
WHERE 在 JOIN 之后才跑,这时候 6、9、10 号那三行的 o.id 是 NULL,别的行都有订单编号。记得要写 IS NULL,写成 = NULL 一行也查不到(第 5 课讲过)。
这条查询查出什么?
1SELECT p.name2FROM products AS p3LEFT JOIN order_items AS oi ON oi.product_id = p.id4WHERE oi.order_id IS NULL;
order_items 是每个订单买了什么。没被买过的商品在里面配不上任何一行,右边补 NULL,WHERE 只留下这些行。这家店只有 6 号台灯没人买过。
每人几单
LEFT JOIN 再加 GROUP BY,就能数出每个客户下了几单,0 单的也在:
1SELECT c.id, c.name, COUNT(o.id) AS order_count2FROM customers AS c3LEFT JOIN orders AS o ON o.customer_id = c.id4GROUP BY c.id, c.name5ORDER BY c.id;
| id | name | order_count |
|---|---|---|
| 1 | 林晓 | 3 |
| 2 | 王磊 | 2 |
| 3 | 陈静 | 1 |
| 4 | 张伟 | 2 |
| 5 | 刘洋 | 2 |
| 6 | 赵敏 | 0 |
| 7 | 孙强 | 1 |
| 8 | 周婷 | 1 |
| 9 | 吴昊 | 0 |
| 10 | 郑雪 | 0 |
这里要写 COUNT(o.id),不能写 COUNT(*)。赵敏那一组只有一行,就是补了 NULL 的那行:COUNT(*) 数行,会数出 1;COUNT(o.id) 不数 NULL,数出 0。把它改成 COUNT(*) 跑一次看看。
条件写在 ON 里
想看每个客户的待发货订单,没有待发货订单的客户也要列出来。直觉是在 WHERE 里加条件,可这样又把客户弄丢了:
1SELECT c.name, o.id AS order_id2FROM customers AS c3LEFT JOIN orders AS o ON o.customer_id = c.id4WHERE o.status = '待发货'5ORDER BY c.id;
| name | order_id |
|---|---|
| 林晓 | 9 |
| 王磊 | 7 |
| 张伟 | 12 |
只剩 3 行。补了 NULL 的行,o.status 也是 NULL,过不了 WHERE。把条件挪进 ON,它就成了「配对的条件」:只拿待发货的订单来配,配不上的客户照样留下:
1SELECT c.name, o.id AS order_id2FROM customers AS c3LEFT JOIN orders AS o ON o.customer_id = c.id AND o.status = '待发货'4ORDER BY c.id;
| name | order_id |
|---|---|
| 林晓 | 9 |
| 王磊 | 7 |
| 陈静 | NULL |
| 张伟 | 12 |
| 刘洋 | NULL |
| 赵敏 | NULL |
| 孙强 | NULL |
| 周婷 | NULL |
| 吴昊 | NULL |
| 郑雪 | NULL |
小技巧: 用 LEFT JOIN 时,关于右边表的条件写在 ON 里;写在 WHERE 里,LEFT JOIN 就和 JOIN 差不多了。
RIGHT JOIN
RIGHT JOIN 反过来:右边的表每一行都留下。orders RIGHT JOIN customers 和 customers LEFT JOIN orders 查出的是同样的行。大多数人只用 LEFT JOIN,把想全部留下的表写在 FROM 后面,读起来更顺。
练一练
写好查询,点检查。提示随便看,实在想不出来再看答案。
没人买的商品
✓用 LEFT JOIN 查出从来没出现在 order_items 里的商品,显示 id 和 name 两列。
| id | name |
|---|---|
| 6 | 台灯 |
1SELECT p.id, p.name2FROM products AS p3JOIN order_items AS oi ON oi.product_id = p.id;
每件商品卖了几件(含 0 件)
✓查出每件商品一共卖出几件:name 和 sold 两列,没卖出的显示 0,按商品 id 排序。
| name | sold |
|---|---|
| 笔记本 | 10 |
| 中性笔(10 支) | 3 |
| 双肩包 | 2 |
| 水杯 | 3 |
| 耳机 | 2 |
| 台灯 | 0 |
| 数据线 | 3 |
| 咖啡杯 | 6 |
| 手机壳 | 2 |
| 帆布袋 | 2 |
1SELECT p.name, SUM(oi.quantity) AS sold2FROM products AS p3LEFT JOIN order_items AS oi ON oi.product_id = p.id4GROUP BY p.id, p.name5ORDER BY p.id;
修好它:少了一个客户
✓想列出北京的每个客户和他们的订单编号,没下过单的客户也要有一行(订单编号是 NULL)。这条查询少了一个客户,改好它。
| name | order_id |
|---|---|
| 王磊 | 2 |
| 王磊 | 7 |
| 赵敏 | NULL |
1SELECT c.name, o.id AS order_id2FROM customers AS c3JOIN orders AS o ON o.customer_id = c.id4WHERE c.city = '北京'5ORDER BY c.id, o.id;
这一题的条件写在 WHERE 里没问题:c.city 是左边表的列,补 NULL 的只是右边。
这一课学会了
JOIN只留配得上的行;LEFT JOIN把左边的表全部留下,配不上的右边补NULL。LEFT JOIN … WHERE 右边.id IS NULL找出「没有配对」的行:没下过单的客户、没人买的商品。- 和
GROUP BY一起数数时用COUNT(右边的列),COUNT(*)会把补NULL的那行也数成 1。 - 右边表的条件写在
ON里;写在WHERE里会把补NULL的行又删掉。 RIGHT JOIN留下右边的表,一般换个顺序写成LEFT JOIN。