前面我们分别使用过 users、products 和 orders,但查询大多只针对一张 Table。真实项目不会把所有信息强行塞进一张大表:不同数据有不同结构和用途,混在一起会造成重复,也让修改越来越麻烦。关系型数据库的核心思路之一,就是把不同类型的数据拆开保存,再通过字段之间的关系连接起来。
一张“大表”看似方便
下面每一行都能看出谁买了什么,但 Alice 的姓名和邮箱随订单重复。她改邮箱时,所有历史订单都要跟着修改;漏掉一行,数据就互相矛盾。
| order_id | user_name | user_email | product_name | quantity |
|---|---|---|---|---|
| 1 | Alice | alice@example.com | Mouse | 1 |
| 2 | Alice | alice@example.com | Coffee Mug | 2 |
| 3 | Bob | bob@example.com | Keyboard | 1 |
拆开保存,按需组合
更常见的设计把用户只保存在 users,商品只保存在 products,订单只记录对应的 user_id、product_id 和数量。订单不再复制 Alice 的资料,而是保存 user_id = 1。需要详细信息时,再根据这个 ID 查询用户。
| id (PK) | name | |
|---|---|---|
| 1 | Alice | [email protected] |
| id (PK) | user_id (FK) | product_id (FK) | quantity |
|---|---|---|---|
| 1 | 1 | 2 | 1 |
| id (PK) | name | price |
|---|---|---|
| 2 | Mouse | 40 |
orders.user_id → users.id一位用户可对应多条订单orders.product_id → products.id一种商品可出现在多条订单中主键与外键
主键(Primary Key) 唯一识别一条 Row。users.id 很适合做主键,因为即使两个用户都叫 Alice,不同 ID 仍能区分他们。主键最重要的特点是唯一:两位用户不能同时拥有相同的 id。
外键(Foreign Key) 是指向另一张表主键的字段。orders.user_id 指向 users.id,告诉数据库订单属于谁;orders.product_id 指向 products.id,说明买了哪个商品。
上图中的两条连接分别是 orders.user_id → users.id 和 orders.product_id → products.id。箭头左侧是订单保存的外键,右侧是被引用表中唯一的主键。
为什么更容易维护?
Alice 改邮箱时,只需修改 users 中的一条 Row。订单仍保存 user_id 1,关系没有改变。商品改名也只修改 products,不必改写每一条历史订单。这样减少重复,也降低不同记录互相矛盾的风险。
UPDATE users
SET email = '[email protected]'
WHERE id = 1;一对多关系
一个用户可以拥有多个订单,而每个订单通常只属于一个用户,这叫 一对多关系(One-to-Many)。在这个简化模型中,一个商品也能出现在多条订单里。现在不必深入数据库设计理论,只要抓住核心:不同 Table 通过 ID 建立关系,下一节的 JOIN 会把分散的数据重新组合。
例如,users.id = 1 的 Alice 可以被订单 1、2、5 的 user_id 重复引用;这就是“一位用户对应多条订单”,而不是把 Alice 的资料复制三遍。
连接真正相关的字段
先点一个字段,再点另一张表中与它有关的字段。完成两条关系。注意:关系靠 ID,而不是靠看起来相似的名称。
尚未建立关系(0 / 2)
先选择 orders 中的外键,再选择它真正引用的主键。修复重复的订单数据
一家咖啡店把 order_id、customer_name、customer_email、drink_name、drink_price、quantity 全放在 orders。请设计 customers、drinks、orders 三张表:列出每张表的字段,标出主键和外键,并说明顾客改邮箱时要改几条记录。
选择可靠的键
在线商店允许两位用户同名,也允许商品改名。判断下面哪些关系可靠,并解释原因:
① orders.user_name → users.name;
② orders.user_id → users.id;
③ orders.product_name → products.name;
④ orders.product_id → products.id。