🦏 第10章 多表查询
📄 一句话概括

数据按「一件事一张表」拆开存,所以看全貌就得把表连起来。 连起来的本质是「按条件把行两两配对」。 不写配对条件就是笛卡儿积:12 个学生 × 3 个班 = 36 行, 其中只有 12 行是真的同班,其余全是乱配。 四类连法:内连接(两边都配上)、外连接(一边没配上也要留)、 笛卡儿积(直接全配)、自连接(一张表跟自己连)。

📝 本章和前面几章的关系

前九章全在「一张表以内」打转。 这一章第一次让两张表见面,也是全书的分水岭。 为什么前面要拆表: 不拆就会有大量重复—— 每个学生都存一遍班级名和班主任,改一个老师要改几十行。 拆开是一张表一件事,代价是查的时候要连。

一、单表查询的天花板

学生表里只有 class_id(数字),没有班级名、没有老师名。 想知道「张伟在哪个班、班主任是谁」,这一张表答不了。 这是拆表的必然结果: 班级信息在 classes、学生信息在 students、成绩在 scores。 每张表只管一件事,所以任何一件「跨了两件事」的问题,都要连表。 合起来存行不行:能跑,但会有大量重复。 三个班 12 个学生,班级名和老师名各存 12 遍;老师一换,12 行都得改,漏一行就自相矛盾。 这就是数据库设计里说的「冗余」。

二、连起来的本质:配对

两个表连起来,就是「按某个条件把行两两配对」。 条件写 s.class_id = c.id,意思就是「学生的班级号等于班级的编号」。 配上的行合成一行,于是「班级名」「班主任」这些本来在另一张表里的列, 就能跟学生信息出现在同一行里了。 所以连表的关键永远是「两个表的哪些列能对上」——这就是外键存在的意义: 它明确告诉你 students.class_id 指向 classes.id,不用猜。 第 9.8 篇讲的外键,在这里兑现了它的价值。

三、不写配对条件会怎样:36 行

FROM students, classes 不写 where,给 36 行。 12 个学生 × 3 个班,每一行都跟对方每一行配了一次。 这叫「笛卡儿积」。 其中只有 12 行是「真的同班」,其余 24 行都是乱配的。 但它不报错—— 这是多表查询最经典的错误:结果行数暴增,看着像「数据变多了」,实际全是垃圾。 排查办法很土但很有效: 先单独 count(*) 两张表,再 count(*) 连完的结果,对不上就是条件写错或漏写了。

四、四类连法,先记住名字

内连接:只留「两边都配上」的行。这是最常用的,占实际工作的八成以上。 外连接:留「一边没配上」的行,另一边的列填 NULL。 分左外、右外、完全外三种。 笛卡儿积连接:不做配对、直接全配。除了算组合和造日历,基本是错误。 自连接:一张表跟自己连,靠两个别名区分。 处理「同一类东西之间的关系」——上下级、同班同学、相邻两天。

五、本章六篇怎么排

10.2 集合运算 → 10.3 内连接 → 10.4 外连接 → 10.5 笛卡儿积 → 10.6 自连接。 集合运算是「上下拼」:把两个结果摞起来,列不变、行变多。 连接是「左右拼」:把两边的列并到一行里,列变多。 这两件事经常被混为一谈,其实完全不同。 一个改行、一个改列,用错场景时结果会非常奇怪。

六、动手 Demo:单表的天花板、一次成功配对、一次 36 行的乱配

Demo 四段:先看单表能给出什么,再把学生表和班级表按 class_id 配对,然后故意不写条件, 看 36 行是怎么来的,最后过一遍四类连法。 第三段那个 36 行是全篇的重点——它说明「连表出错」不会报错,只会让数字变大。

import _q46 as Q
c = Q.con()

# 单表只能看到班级号,看不到班级名
Q.show(c, "SELECT id, name, class_id FROM students LIMIT 3")

# 连起来:按 s.class_id = c.id 配对
Q.show(c, "SELECT s.name AS 学生, c.name AS 班级, c.teacher AS 班主任 "
          "FROM students s JOIN classes c ON s.class_id = c.id LIMIT 4")

# 不写配对条件:12 x 3 = 36 行
Q.show(c, "SELECT COUNT(*) AS rows_no_on FROM students, classes")

实跑输出:

--------------------------------------------------------------------
第五十二课:一张表看不全,所以要把表连起来
--------------------------------------------------------------------

  单表查询的天花板
  SQL> SELECT id, name, class_id FROM students LIMIT 3
  id     name     class_id
  -------------------------
  1      张伟     1
  2      王芳     1
  3      李娜     2
  (3 行)
  **学生表里只有 `class_id`,没有班级名、没有老师名。**
  **想知道「张伟在哪个班、班主任是谁」,这张表答不了。**
  **数据是按「一件事一张表」拆开存的**:
  **班级的信息在 `classes`、学生的信息在 `students`、成绩在 `scores`。**
  **拆开的好处是不重复、改一处就够;代价是要看全貌就得连起来。**

  连起来的本质:配对
  **两个表连起来,就是「按某个条件把行两两配对」。**
  **条件写 `s.class_id = c.id`,意思就是「学生的班级号 等于 班级的编号」。**
  SQL> SELECT s.name AS 学生, c.name AS 班级, c.teacher AS 班主任 FROM students s JOIN classes c
       ON s.class_id = c.id LIMIT 4
  学生     班级     班主任
  -------------------------
  张伟     一班     马老师
  王芳     一班     马老师
  李娜     二班     孔老师
  刘洋     二班     孔老师
  (4 行)
  **一行学生配上了一行班级**,于是「班级名」和「班主任」都能拿出来了。

  不写配对条件会怎样:36 行
  SQL> SELECT COUNT(*) AS rows_no_on FROM students, classes
  rows_no_on
  -----------
  36
  (1 行)
  **12 个学生 × 3 个班 = 36 行。** **这叫「笛卡儿积」——每一行都跟对方每一行配了一次。**
  **其中只有 12 行是「真的同班」,其余 24 行都是乱配的。**
  **这是多表查询最经典的错误**:忘了写条件,
  **结果不报错、行数暴增,看着像「数据变多了」,实际全是垃圾。**

  三种连接,先记住名字
  **内连接**:只留「两边都配上」的行。**这是最常用的。**
  **外连接**:留「一边没配上」的行,另一边的列填 NULL。 **分左外、右外、完全外三种。**
  **笛卡儿积连接**:不做配对、直接全配。**除了算组合,基本是错误。**
  **外加一种「自连接」**:**一张表跟自己连**,用来处理「同一类东西之间的关系」。
  **这一章六篇就是这四类。**

  本章六篇怎么排
  **10.2 集合运算 → 10.3 内连接 → 10.4 外连接 → 10.5 笛卡儿积 → 10.6 自连接。**
  **集合运算是「上下拼」**(把两个结果摞起来),**连接是「左右拼」**(把两边的列并到一行)。
  **这两件事经常被混为一谈,其实完全不同。**

第一段三行学生,只有 class_id,没有班级名和老师名。 第二段配对成功,四行结果里班级名和班主任都出来了。 第三段 36 行:12 × 3,只有三分之一是真的同班。 后三段没有 SQL,讲四类连法和这一章的篇目顺序。

30 秒实验

  1. 为什么学生表里不直接存班级名和班主任?那样不是更省事吗?
  2. SELECT * FROM students, classes 会给出几行?为什么?
  3. 集合运算和连接,一个改行一个改列——各举一个该用的场景。

常见误区

  1. 连表忘写配对条件。 不报错,只是行数变成乘积,报表数字凭空变大。
  2. 把集合运算当连接用。 UNION 是上下摞行,不会把两边的列并到一行,需求会完全走偏。
  3. 以为「连表越多越强」。 每多一张表就多一次配对,条件漏一个行数就炸一次。该分步查的就分步。
自测三问(点开对答案) 1. 为什么要把数据拆成多张表?代价是什么? —— 拆开避免冗余,改一处就够;代价是查跨表信息时必须连表,写起来更复杂。 2. 两个表连起来的本质是什么? —— 按某个条件把行两两配对,配上之后两边的列就能出现在同一行里。 3. 四类连法分别是什么? —— 内连接(只留两边都配上的)、外连接(一边没配上也要留)、笛卡儿积(直接全配)、 自连接(自己跟自己连)。

先讲一个跟连接完全不同、但经常被混进来的东西: 下一篇 10.2 集合运算——把两个结果上下摞起来。

相关笔记