Kodewick
第 12 课,共 21 课目录

LEFT JOIN

第 12 课,共 21 课 · 第 3 部分 · 多张表 · 约 15 分钟

上一课的 JOIN 只留下两边配得上的行。可很多问题偏偏要问配不上的那些:哪些客户一单都没下过?哪件商品从来没人买?这一课讲 LEFT JOIN,它把左边的表一行不少地留下来。

配不上的行没了

先用上一课的写法,把客户和订单接起来:

main.sql
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;
输出
idnameorder_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 行

结果有 12 行,一个订单一行。客户一共 10 个,可 6、9、10 号客户不在里面:他们没下过单,在 orders 里找不到配对的行,JOIN 就把他们丢掉了。

LEFT JOIN

把 JOIN 换成 LEFT JOIN,别的都不改:

main.sql
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;
输出
idnameorder_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 行

现在是 15 行。6、9、10 号客户回来了,他们的 order_id 是 NULL。

LEFT JOIN 的意思是:写在 FROM 后面的那张表(左边的表)每一行都要留下。配得上的,和上一课一样接上右边的行;配不上的,也留下一行,右边的列全部填 NULL。

左边这一行JOINLEFT JOIN
配上 1 行1 行1 行
配上 3 行3 行3 行
一行也配不上没了1 行,右边全是 NULL

单步看补 NULL

点上面那段查询的单步,看 JOIN 那一步:每个客户去 orders 里找 customer_id 等于自己 id 的订单。1 号林晓找到 3 个,就变成 3 行;走到 6 号赵敏时一个也没找到,她还是留下了一行,右边的格子全是 NULL。

右边的列不止 id。选上订单的所有列,看得更清楚(o.* 就是 o 这张表的所有列):

main.sql
1SELECT c.name, o.*2FROM customers AS c3LEFT JOIN orders AS o ON o.customer_id = c.id4WHERE c.id >= 85ORDER BY c.id;
输出
nameidcustomer_idorder_datestatus
周婷1082025-10-20已发货
吴昊NULLNULLNULLNULL
郑雪NULLNULLNULLNULL
3 行

周婷有一个订单,四列都有值;吴昊和郑雪没有订单,orders 的四列都是 NULL。

没下过单的客户

既然配不上的行右边是 NULL,只留这些行,就是「没下过单的客户」:

main.sql
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;
输出
idname
6赵敏
9吴昊
10郑雪
3 行

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;

每人几单

LEFT JOIN 再加 GROUP BY,就能数出每个客户下了几单,0 单的也在:

main.sql
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;
输出
idnameorder_count
1林晓3
2王磊2
3陈静1
4张伟2
5刘洋2
6赵敏0
7孙强1
8周婷1
9吴昊0
10郑雪0
10 行

这里要写 COUNT(o.id),不能写 COUNT(*)。赵敏那一组只有一行,就是补了 NULL 的那行:COUNT(*) 数行,会数出 1;COUNT(o.id) 不数 NULL,数出 0。把它改成 COUNT(*) 跑一次看看。

条件写在 ON 里

想看每个客户的待发货订单,没有待发货订单的客户也要列出来。直觉是在 WHERE 里加条件,可这样又把客户弄丢了:

main.sql
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;
输出
nameorder_id
林晓9
王磊7
张伟12
3 行

只剩 3 行。补了 NULL 的行,o.status 也是 NULL,过不了 WHERE。把条件挪进 ON,它就成了「配对的条件」:只拿待发货的订单来配,配不上的客户照样留下:

main.sql
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;
输出
nameorder_id
林晓9
王磊7
陈静NULL
张伟12
刘洋NULL
赵敏NULL
孙强NULL
周婷NULL
吴昊NULL
郑雪NULL
10 行

小技巧: 用 LEFT JOIN 时,关于右边表的条件写在 ON 里;写在 WHERE 里,LEFT JOIN 就和 JOIN 差不多了。

RIGHT JOIN

RIGHT JOIN 反过来:右边的表每一行都留下。orders RIGHT JOIN customers 和 customers LEFT JOIN orders 查出的是同样的行。大多数人只用 LEFT JOIN,把想全部留下的表写在 FROM 后面,读起来更顺。

练一练

写好查询,点检查。提示随便看,实在想不出来再看答案。

练习 1

没人买的商品

用 LEFT JOIN 查出从来没出现在 order_items 里的商品,显示 id 和 name 两列。

要显示
idname
6台灯
1 行
exercise1.sql
1SELECT p.id, p.name2FROM products AS p3JOIN order_items AS oi ON oi.product_id = p.id;
输出
点「运行」或按 Ctrl+Enter
练习 2

每件商品卖了几件(含 0 件)

查出每件商品一共卖出几件:name 和 sold 两列,没卖出的显示 0,按商品 id 排序。

要显示
namesold
笔记本10
中性笔(10 支)3
双肩包2
水杯3
耳机2
台灯0
数据线3
咖啡杯6
手机壳2
帆布袋2
10 行
exercise2.sql
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;
输出
点「运行」或按 Ctrl+Enter
练习 3

修好它:少了一个客户

想列出北京的每个客户和他们的订单编号,没下过单的客户也要有一行(订单编号是 NULL)。这条查询少了一个客户,改好它。

要显示
nameorder_id
王磊2
王磊7
赵敏NULL
3 行
exercise3.sql
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;
输出
点「运行」或按 Ctrl+Enter

这一题的条件写在 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。