SQL-多对多关系

一、场景背景:多对多关系的业务逻辑

在实际业务中,"学生" 和 "课程" 存在典型的多对多关系

  • 一个学生可以选择多门课程(1 个学生 → N 门课程);
  • 一门课程可以被多个学生选择(1 门课程 → N 个学生)。

这种关系无法直接通过两个表的字段关联实现(会导致数据冗余或关联混乱),因此需要引入中间表 来维护两者的关联关系。上述代码中,student(学生表)和course(课程表)是两个主体表,student_course(学生 - 课程中间表)是关联表。

二、表结构设计与核心语法解析

1. 学生表(student

sql

sql 复制代码
create table student(
    id int auto_increment primary key comment '主键ID',
    name varchar(10) not null unique comment '名字',
    no varchar(10) comment '学号'  -- 注:原代码注释有误,应为"学号"
)comment '学生表';
  • 核心字段解析
    • id:整数类型(int),作为主键(primary key),且设置auto_increment(自增)。
      • 主键(primary key):唯一标识表中的每条记录,确保记录不重复,且加速查询(主键会自动创建索引)。
      • 自增(auto_increment):插入数据时无需手动指定id值,MySQL 会自动生成唯一的递增数值(默认从 1 开始),简化插入操作。
    • name:字符串类型(varchar(10),最长 10 个字符),设置not null(非空,必须填写)和unique(唯一,不允许重复的学生姓名)。
    • no:字符串类型,注释为 "学号"(原代码注释笔误),未设置非空约束,允许为空。
  • comment:为表和字段添加注释,提高代码可读性(可通过show full columns from 表名;查看)。
2. 课程表(course

sql

sql 复制代码
create table course(
    id int auto_increment primary key comment '主键ID',
    name varchar(10) comment '课程名字'
)comment '课程表';
  • 结构与student表类似,核心字段为id(自增主键)和name(课程名称,未设置唯一约束,允许同名课程,具体根据业务需求调整)。
3. 中间表(student_course

中间表是多对多关系的核心,用于记录学生和课程的关联关系:

sql

sql 复制代码
create table student_course(
    id int auto_increment primary key comment '主键',
    student_id int not null comment '学生ID',
    course_id int not null comment '课程ID',
    constraint fk_student_id foreign key (student_id) references student(id),
    constraint fk_course_id foreign key (course_id) references course(id)
)comment '学生课程中间表';
  • 核心设计逻辑

    • 包含两个外键字段:student_id(关联学生表的id)和course_id(关联课程表的id),通过这两个字段建立学生和课程的关联。
    • 自身主键id:中间表也可以用student_idcourse_id作为联合主键primary key(student_id, course_id)),但此处用自增id作为主键,更便于后续对关联记录的单独操作(如删除某条选课记录)。
  • 外键约束(foreign key)详解:外键是维护数据完整性的核心约束,用于确保关联字段的值必须在被关联表中存在。

    • constraint fk_student_id:为外键约束命名(便于后续删除或修改约束)。
    • foreign key (student_id) references student(id):表示student_id的值必须是student表中已存在的id(否则插入 / 更新会失败)。
    • 同理,course_id必须是course表中已存在的id
    • 作用:避免 "无效关联"(如关联一个不存在的学生或课程),保证数据一致性。

三、数据插入(insert)操作解析

通过insert语句向表中插入数据,验证多对多关系的实际存储:

1. 向学生表插入数据

sql

sql 复制代码
insert into student values (null, '带一丝','2000100109'),
                           (null, '谢谢从','2000100107'),
                           (null, '带二四','2000100105'),
                           (null, '带三丝','2000100101');
  • values后的null对应自增主键id,MySQL 会自动生成1、2、3、4(按插入顺序)。
  • 插入后学生表数据:
id name no
1 带一丝 2000100109
2 谢谢从 2000100107
3 带二四 2000100105
4 带三丝 2000100101
2. 向课程表插入数据

sql

复制代码
insert into course values (null, 'mysql'),(null,'PHP'),(null, 'Java'),(null, 'Hadoop');
  • 课程表id自动生成1、2、3、4,对应课程名:
id name
1 mysql
2 PHP
3 Java
4 Hadoop
3. 向中间表插入关联数据

sql

sql 复制代码
insert into student_course values (null,1,1 ),(null,1,2),
                                  (null, 2,2),(null, 2,3),
                                  (null,3,4);
  • 每条记录代表 "某个学生选了某门课":

    • (null,1,1):学生 1(带一丝)选了课程 1(mysql);
    • (null,1,2):学生 1 选了课程 2(PHP);
    • (null,2,2):学生 2(谢谢从)选了课程 2(PHP);
    • (null,2,3):学生 2 选了课程 3(Java);
    • (null,3,4):学生 3(带二四)选了课程 4(Hadoop)。
  • 插入后中间表数据:

id student_id course_id
1 1 1
2 1 2
3 2 2
4 2 3
5 3 4
  • 注意:学生 4(带三丝)未选课,课程 1(mysql)仅学生 1 选,课程 4(Hadoop)仅学生 3 选,体现了多对多关系的灵活性。

四、多对多关系的查询场景(延伸)

通过中间表可以查询多对多关系的具体数据,例如:

  1. 查询 "带一丝" 选了哪些课程:

sql

sql 复制代码
select s.name, c.name 
from student s
join student_course sc on s.id = sc.student_id
join course c on sc.course_id = c.id
where s.name = '带一丝';

结果:

name name
带一丝 mysql
带一丝 PHP
  1. 查询 "PHP" 课程被哪些学生选了:

sql

sql 复制代码
select c.name, s.name 
from course c
join student_course sc on c.id = sc.course_id
join student s on sc.student_id = s.id
where c.name = 'PHP';

结果:

name name
PHP 带一丝
PHP 谢谢从

五、总结

  1. 多对多关系设计核心:通过 "主体表 A + 主体表 B + 中间表(含 A 和 B 的外键)" 实现,避免数据冗余。
  2. 约束的作用
    • 主键(primary key):唯一标识记录,加速查询。
    • 自增(auto_increment):简化主键值的生成。
    • 非空(not null)和唯一(unique):保证字段数据的有效性。
    • 外键(foreign key):维护关联数据的一致性,防止无效关联。
  3. 中间表的意义:作为多对多关系的 "桥梁",存储两个主体表的关联记录,是查询关联数据的核心。

通过上述设计,MySQL 能高效维护学生与课程的多对多关系,并支持灵活的关联查询。

相关推荐
Kapaseker2 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴2 小时前
Android17 为什么重写 MessageQueue
android
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
Duang1 天前
从零推导指数估值模型 —— 一个三因子打分系统的设计思路
数据分析·领域驱动设计
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android