本节摘要:主键唯一标识一行,外键把表关联起来。这两个概念是关系型数据库"关系"二字的来源。本节讲清它们的作用、怎么定义、以及一对一/一对多/多对多关系。
阅读完本节,你应当能够:
主键(Primary Key)是一列(或几列)的组合,能唯一标识表里的每一行,且不能为 NULL。比如 Customers 表的 CustomerID——每个客户一个唯一 ID,靠它定位某一行。
主键的要求:
CREATE TABLE Customers ( CustomerID INT PRIMARY KEY, FirstName VARCHAR(50), ... );
主键可以是单列,也可以是多列组合(复合主键),比如订单明细表用 (OrderID, ProductID) 组合唯一标识一行。
外键(Foreign Key)是一列,它的值引用另一张表的主键,表示"这行属于那个对象"。比如 Orders 表的 CustomerID 引用 Customers 的 CustomerID——某条订单属于某个客户。

外键的作用是建立表间关系 + 保证参照完整性。参照完整性指:外键值必须在被引用表的主键里存在,不能引用一个不存在的对象。比如不能给一个不存在的客户 ID 插订单。
CREATE TABLE Orders ( OrderID INT PRIMARY KEY, CustomerID INT, Amount DECIMAL(10,2), FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID) );
表之间的关系按"一端能对应几条"分三种:
一个客户有多条订单,一条订单只属于一个客户。Customers 对 Orders 是一对多。这是最常见的,靠在"多"的一方加外键实现(Orders.CustomerID)。
一行只对应一行。比如用户表和用户详情表,分开存以减少主表宽度。靠在从表加唯一外键实现。
学生和课程:一个学生选多门课,一门课有多学生。关系型不能直接表达多对多,要用一张中间表(选课表)拆成两个一对多。
| 关系 | 例子 | 实现方式 |
|---|---|---|
| 一对多 | 客户-订单 | 多方加外键 |
| 一对一 | 用户-详情 | 从表加唯一外键 |
| 多对多 | 学生-课程 | 中间表拆成两个一对多 |
外键保证数据一致性,但有代价:插入/删除/更新要检查约束,性能略降;级联操作可能引发连锁。所以有些高并发系统不用外键约束,改在应用层保证一致性。入门阶段建议用外键,理解了再权衡。
⚠️ 常见坑:建了外键后删被引用的行报错(有子记录依赖)。要么先删子记录,要么用
ON DELETE CASCADE级联删(慎用,会连锁删一堆)。
💡 关键直觉:主键管"唯一标识自己",外键管"和别人建立关系"。关系型数据库的"关系"就是靠外键织起来的网。
第 2 章结束。概念地基铺好了,下一章开始写真正的查询语句——SELECT。
Q1:主键必须用自增整数吗?
不是必须,但很常见。自增整数小而快,适合大多数场景。有些场景用业务唯一键当主键(如身份证号、手机号),但业务键可能变更、可能泄露,长期看不如代理主键稳定。另一种是 UUID,适合分布式环境,缺点是占空间大、索引效率略低。入门阶段建议先用自增整数,理解原理后再按需选型。
Q2:外键约束一定要加吗?
从数据一致性角度,建议加。外键约束让数据库替你保证"不能引用不存在的行",避免脏数据。缺点是每次插入/删除要检查,性能略降;高并发系统有时为了性能去掉外键,改在应用层保证一致性——但那是高级场景的权衡,初学者先把外键用起来。
Q3:怎么理解"多对多"一定需要中间表?
因为关系模型里,一行只能存一个外键指向一个对象。学生和课程是多对多:一个学生有多门课,一门课有多个学生。无论把外键放哪张表都无法表达。所以建一张"选课表",每行存一对(学生ID,课程ID),把多对多拆成两个一对多。这是关系数据库最经典的设计模式,务必理解。
Q4:ON DELETE CASCADE 到底要不要用?
谨慎。CASCADE 会自动删子表数据,方便但危险——误删一行可能连锁删掉几十行。建议默认不用,明确知道"删父必删子"的强关联场景(如订单明细跟随订单删除)才用。生产环境更常见的做法是软删除或限制删除。