全书目录

第十八篇:MySQL 完全指南 —— 城市户籍总账宇宙

第七章:JOIN —— 去不同账册之间对账

1 分钟 333 字 第 342 / 962 个阅读单元

单表查询只能看一本账册。

而真实业务里,你经常要把“市民”“户号”“行政区”对起来看。这时就要用 JOIN

查某市民属于哪个户、哪个区:

sql
SELECT
  c.citizen_id,
  c.full_name,
  h.household_no,
  d.district_name
FROM citizens AS c
JOIN households AS h
  ON h.household_id = c.household_id
JOIN districts AS d
  ON d.district_id = h.district_id
WHERE c.status = 'active';

这里不是“把表机械拼接”,而是:

拿着一个账册里的编号,去另一册里对号。

常见 JOIN 可以这样记:

  • INNER JOIN:两边能对上的才保留
  • LEFT JOIN:左边全保留,右边对不上就补 NULL

比如,想查所有市民及最近一次迁户记录,没有迁户历史的人也要显示出来:

sql
SELECT
  c.full_name,
  m.reason,
  m.moved_at
FROM citizens AS c
LEFT JOIN move_logs AS m
  ON m.citizen_id = c.citizen_id
WHERE c.status = 'active';

LEFT JOIN 的经典坑是:把右表过滤条件写进 WHERE,结果把左边“没匹配上的行”也过滤掉了。

例如下面这句就很危险:

sql
SELECT c.full_name, m.reason
FROM citizens AS c
LEFT JOIN move_logs AS m
  ON m.citizen_id = c.citizen_id
WHERE m.reason = '升学迁入';

它常常会让结果看起来像 INNER JOIN

如果你真想保留左边所有市民,右边只匹配某类记录,通常应该把条件放进 ON

sql
SELECT c.full_name, m.reason
FROM citizens AS c
LEFT JOIN move_logs AS m
  ON m.citizen_id = c.citizen_id
 AND m.reason = '升学迁入';