多对多关系与连接表
多对多关系通过连接表把 N:N 拆成两个 1:N,并允许把角色、状态、时间等关系属性存到关系本身。
#concept
#growing
#backend
#database
#sql
[!info] related notes
- 所属 MOC: database-moc
- 前置概念: one-to-many-relationship、foreign-key
- 并列概念: composite-unique-constraint
- 易混淆概念:
- 关系笔记:
多对多关系与连接表
一句话定义
多对多(N:M)表示一个 A 可以关联多个 B,同时一个 B 也可以关联多个 A;在关系数据库中通常通过第三张连接表实现。
核心模型
Student 1 ─── N Enrollment N ─── 1 Course
因此数据库里的 N:M 实际被拆成两个 1:N。
CREATE TABLE enrollments (
student_id BIGINT NOT NULL REFERENCES students(id),
course_id BIGINT NOT NULL REFERENCES courses(id),
UNIQUE (student_id, course_id)
);
为什么需要连接表
把多个 ID 塞进 students.course_ids 或 courses.student_ids 会让引用完整性、索引、JOIN 和约束都变得困难。连接表让“一条关系”本身成为一行,可以被查询和约束。
关系本身也可以有属性
如果选课还需要记录:
enrolled_at
grade
status
这些字段描述的是“某个 Student 与某门 Course 的关系”,应放在 enrollments,而不是 students 或 courses。
同理:
Membership.roleReadingList.readOrderItem.quantity
一旦连接关系拥有自己的状态、生命周期或行为,它往往已经是一个真正的领域实体,而不只是技术胶水。
唯一性与主键
常见两种设计:
(student_id, course_id)直接作为复合主键;- 使用独立
id主键,同时增加UNIQUE (student_id, course_id)。
选择取决于关系是否需要被其他表单独引用、ORM 使用习惯和领域身份需求。
边界与常见误解
- N:M 是关系的基数,不等于必须使用某个 ORM 的
belongsToManyAPI。 - 连接表上的属性属于关系;课程自身的
credits仍应放courses。 - 如果同一对实体允许多次建立有意义的关系(如同一学生可多学期重修同一课程),
UNIQUE(student_id, course_id)可能过强,应把term等维度纳入唯一键。