LEFT JOIN 与保留未匹配行
了解 LEFT JOIN 会保留哪些内容,以及不匹配行中如何出现 NULL
LEFT JOIN 与保留未匹配行 是 CoddyKit 上的免费 Coding Interview Prep 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Coding Interview Prep 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Coding Interview Prep 课程共包含 4 节课。
面试场景
面试官说:“列出每位客户及其订单,包括从未下过订单的客户。”INNER JOIN 会丢弃没有订单的客户,因此无法满足这个要求。这里应使用 LEFT JOIN。
LEFT JOIN 会保留左侧(首先写出的)表中的每一行,并在右表存在匹配行时附加这些行。本课将帮助您建立面试官所期望的准确思维模型。
LEFT JOIN 的保证
核心保证是:左表中的每一行都会至少在输出中出现一次,无论是否匹配。
- 如果左表中的一行在右表找到匹配项,输出中会为每个匹配项生成一行。
- 如果左表中的一行没有找到匹配项,输出中仍会有一行,并且右表的所有列都会设为
NULL。
这就是面试官称其为外连接的原因:匹配集合之外的行会被保留,而不是丢弃。
两张表
本课始终使用两张简单的表。customers 包含所有客户;orders 记录购买行为,并通过 customer_id 引用客户。
请注意,客户 3(卡罗尔)在 orders 中没有对应的行。这个未匹配的客户正是 LEFT JOIN 必须保留的对象。
-- customers
-- id | name
-- 1 | Alice
-- 2 | Bob
-- 3 | Carol
-- orders
-- id | customer_id | amount
-- 10 | 1 | 50
-- 11 | 1 | 75
-- 12 | 2 | 20编写 LEFT JOIN
语法与 INNER JOIN 类似,只需将 INNER 换成 LEFT(OUTER 这个词可以省略:LEFT JOIN 和 LEFT OUTER JOIN 完全相同)。
在 JOIN 关键字之前写出的表就是被保留的左侧。顺序很重要:customers LEFT JOIN orders 会保留所有客户,而不是所有订单。
SELECT c.name, o.amount
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.id;读取结果
下面是该查询的输出结果。爱丽丝匹配了两次,因此出现两行。鲍勃匹配了一次。卡罗尔没有匹配到任何内容,因此仍然会出现,并且 amount 列的值为 NULL。
这唯一一行带有 NULL 的卡罗尔记录,就是 LEFT JOIN 的典型特征。面试官希望听到您这样解释:“未匹配的左表行会保留,右表列则为 NULL。”
-- name | amount
-- Alice | 50
-- Alice | 75
-- Bob | 20
-- Carol | NULL <-- preserved, no matchNULL 从何而来
这个 NULL 并没有存储在任何地方。由于没有右表行可以提供 amount,数据库会合成它。
对于未匹配的左表行,从右表获取的每一列都会变成 NULL:o.id、o.customer_id 和 o.amount 在卡罗尔这一行中都为 NULL。这一点很重要,因为 o.customer_id 为 NULL 并不意味着该客户没有 ID,而是意味着根本没有匹配的订单。
统计每位客户的订单数
一个常见的后续问题是:“如何统计每位客户的订单数,包括订单数为零的客户?”使用 LEFT JOIN 加上 COUNT 就能准确实现,但您必须统计右表的列,而不是 *。
COUNT(o.id) 会忽略 NULL,因此卡罗尔的结果为 0。COUNT(*) 则会错误地为卡罗尔返回 1,因为合成的 NULL 行仍然会被计为一行。
SELECT c.name, COUNT(o.id) AS order_count
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.name;
-- Alice | 2
-- Bob | 1
-- Carol | 0COUNT(*) 与 COUNT(列) 的陷阱
这是一个经典的面试陷阱。LEFT JOIN 之后,未匹配的左表行会实际存在于结果中,只是右表列为 NULL。
COUNT(*)会统计这一行,因此对于没有订单的客户返回 1。COUNT(o.id)会跳过 NULL,因此正确返回 0。
当您需要得到真正的零值时,请始终统计在无匹配一侧会为 NULL 的列。
-- WRONG: Carol shows 1
SELECT c.name, COUNT(*) AS n
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.name;使用 LEFT JOIN 求和
除了 COUNT 之外的聚合函数也能妥善处理合成的 NULL 值。SUM 会忽略 NULL,因此未匹配的客户会得到 NULL(不会报错)。
面试官通常希望报表显示零而不是 NULL。请将聚合函数包在 COALESCE 中,以替换默认值。
SELECT c.name,
COALESCE(SUM(o.amount), 0) AS total_spent
FROM customers c
LEFT JOIN orders o ON o.customer_id = c.id
GROUP BY c.name;
-- Alice | 125
-- Bob | 20
-- Carol | 0清楚说明左表与右表
当面试官问“哪张表会被保留?”时,请自信地回答:JOIN 关键字左侧的表。请从左到右阅读。
A LEFT JOIN B = 保留 A 的全部内容。如果要保留 B 的全部内容,可以改写为 B LEFT JOIN A,也可以使用 RIGHT JOIN。为了提高可读性,大多数团队只使用 LEFT JOIN,直接重新排列表的顺序。
思维检查清单
在面试中看到 LEFT JOIN 时,请大声说出以下检查清单:
- 保留哪一侧?左表,所有行都会保留。
- 未匹配的行?出现一次,并带有来自右表的 NULL 值。
- 统计数量?使用
COUNT(right_col)才能得到真正的零值。 - 求和?如果需要 0 而不是 NULL,请使用
COALESCE包装。
能够清晰说出这份检查清单,体现出高级水平的严谨性。
快速检查
通过统计来测试您对 LEFT JOIN 的直觉。
回顾
LEFT JOIN 会保留左表中的每一行。已匹配的左表行会与右表的匹配行配对;未匹配的左表行会保留一次,并且右表的所有列都为 NULL。
- 左表就是位于
JOIN关键字之前的表。 - 合成的 NULL 值标记了没有匹配项的行。
COUNT(right_col)能得到真正的零值;COUNT(*)不能。COALESCE(SUM(...), 0)会将 NULL 总计转换为零。
这是后续课程中查找缺失数据和反连接的基础。
常见问题解答
「LEFT JOIN 与保留未匹配行」课时是免费的吗?
是的 — 「LEFT JOIN 与保留未匹配行」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Coding Interview Prep 课程的其余内容,请升级到 CoddyKit PRO。 Coding Interview Prep 课程共包含 4 节课。
「LEFT JOIN 与保留未匹配行」这节课中我会学到什么?
了解 LEFT JOIN 会保留哪些内容,以及不匹配行中如何出现 NULL 你通过在浏览器中直接运行的动手代码来练习 Coding Interview Prep,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Coding Interview Prep 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Coding Interview Prep 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「LEFT JOIN 与保留未匹配行」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Coding Interview Prep 课中编写并运行代码吗?
能。每节 Coding Interview Prep 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- LEFT JOIN 与保留未匹配行
- RIGHT 和 FULL OUTER JOIN 语义
- 查找没有匹配项的行(反连接)
- 外连接中的 WHERE 筛选陷阱