题目描述

✅ 175. 组合两个表

image-20260928233436896

image-20260928233436899

题意分析

Person 保存人员编号、姓和名,Address 保存人员编号对应的城市和州。结果必须包含 Person 中的每个人;找不到地址的人也要保留,只把城市和州显示为 NULL。

因此要先确定哪些行不能丢:是人员记录,而不是地址记录。将 Person 放在左侧做 LEFT JOIN,恰好能保留这些行;普通内连接只返回匹配成功的人员,会漏掉没有地址的人。

两表用 personId 关联。Address.addressId 标识地址记录本身,不能拿它与人员编号直接比较。输出只需要姓名、城市和州,无需分组或排序。

解法:以 Person 为主表做 LEFT JOIN

核心思路

[!blue]

对 Person AS p 中的每个人,按照 a.personId = p.personId 查找地址。找到匹配行时,把姓名和对应的地址字段组合起来;一个匹配行也没有时,左连接仍输出该人员,并把右表字段补为 NULL。

这样每条人员记录至少出现一次,而只存在于 Address、在 Person 中找不到对应人员的记录不会被独立输出。若同一个人匹配多条地址,连接会按每条地址展开多行;题目没有要求任选一条,因此不应擅自聚合或去重。

ON 中只写两张表的匹配关系。最终不再添加右表过滤条件,才能保留左连接补出的空值行。

解题步骤

  1. 从 Person AS p 开始查询,确定必须保留的左表。
  2. 用 LEFT JOIN Address AS a 连接地址表。
  3. 在 ON 中要求两边 personId 相同。
  4. 依次选择 firstName、lastName、city、state;未匹配时后两列会自然为 NULL。

样例中 Allen 没有地址,所以仍保留姓名并补两个 NULL;Bob 匹配到纽约地址;地址表中人员编号为 3 的记录没有对应人员,不会单独出现在结果中。

代码实现

SELECT
    p.firstName,
    p.lastName,
    a.city,
    a.state
FROM Person AS p
LEFT JOIN Address AS a
    ON a.personId = p.personId;

复杂度分析

设两张表分别有 $P$、$A$ 行,结果有 $R$ 行。SQL 的实际开销取决于索引与执行计划,不能仅从语句确定唯一复杂度。

  • 时间复杂度:以哈希左连接为例,扫描两表并输出结果约为 $O(P+A+R)$;朴素嵌套循环则可能达到 $O(PA+R)$。若右表连接列有索引,可以通过索引查找减少匹配工作。
  • 空间复杂度:若为右表构建哈希表,额外空间为 $O(A)$,不计结果。使用其他连接算法时,临时空间也会相应变化。

关键点总结

[!green]

  • “没有地址也要输出”直接决定使用保留人员表的外连接。
  • NULL 由外连接补出,无需 CASE 或自行构造默认值。
  • 连接键表达表之间的人员关系,不能因为两列都是主键就拿来连接。
  • 没有匹配与匹配多行是两种情况:前者补一行空值,后者保留每个匹配结果。

易错点总结

[!yellow]

  • 使用 INNER JOIN:没有地址的人员会消失。
  • 把 Address 放在保留侧:保留的是地址记录,不能保证所有人员出现。
  • 连接到 addressId:它是地址编号,不是地址所属人员的编号。
  • 在 WHERE 中筛选右表城市:未匹配行中的城市是 NULL,通常无法通过该筛选;本题不需要这种额外条件。
  • 添加 DISTINCT 或按姓名分组:不同人员可能同名,连接结果也可能有多条地址,擅自合并会改变题意。
  • 使用 SELECT *:会带上题目没有要求的编号列,应明确选择四个输出字段。

相似题目

题目 难度 关联与区别
183. 从不订购的客户 简单 同样利用LEFT JOIN保留未匹配主表行,本题输出全部人员,原题只筛选未匹配者。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/68757257
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!