多表关系概览
在项目开发中,进行数据库表结构设计时,会根据业务需求及业务模块之间的关系,分析并设计表结构,因为业务之间相互关联,所以说各个表结构之间也存在着各种联系,基本上分为:一对多(多对一)、多对多、一对一这三种。
一对多
假如说需要开发一个员工管理系统,那么肯定是需要两张数据库表:员工表和部门表;而且部门和员工之间的关系是一对多:因为一个部门可以对应多个员工,而一个员工一般只对应一个部门。那么这个时候这两张表就产生了一对多的关系。
首先需要新建两张数据库表,一张是部门表,一张是员工表:
create table tb_dept(
id int unsigned primary key auto_increment comment '主键ID',
name varchar(10) not null unique comment '部门名称',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间'
) comment '部门表';
员工表:
create table tb_emp (
id int unsigned primary key auto_increment comment 'ID',
username varchar(20) not null unique comment '用户名',
password varchar(32) default '123456' comment '密码',
name varchar(10) not null comment '姓名',
gender tinyint unsigned not null comment '性别, 说明: 1 男, 2 女',
image varchar(300) comment '图像',
job tinyint unsigned comment '职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管',
entrydate date comment '入职时间',
dept_id int unsigned comment '部门ID',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间'
) comment '员工表';
通过员工表中的的dept_id字段标识该员工的所属部门,也就是通过dept_id字段使得两张表产生了联系。但只是这样写,在逻辑上两张表确实产生了关系,但是在数据库层面而言,两张表并未建立任何关联:因为直接这么写,就算我们知道两张表的逻辑,数据库也是不知道的。此时若对部门表进行操作,就会产生这样的问题:部门表中的部门可以直接删除,但是此时仍然还有部分员工归属于该部门,但是部门已经删除了。这就导致了数据的不完整、不一致的问题。由此可见,若两张表只在逻辑上有关联,在数据库的层面并未建立关联,无法保证数据的一致性和完整性。
解决方法
使用外键约束,通过对表中的字段添加外键约束————foreign key,可以使得两张表在物理层面上建立关联。
建表之前给字段添加外键:
create table 表名(
字段名 数据类型 constraint 外键名 foreign key (外键字段名) reference 主表 (字段名)
)
建表之后给字段添加外键:
alter table 表名 add constraint 外键名 foreign key (外键字段名) reference 主表(字段名)
比如说这样;
create table emp (
id int unsigned primary key auto_increment comment 'ID',
username varchar(20) not null unique comment '用户名',
password varchar(32) default '123456' comment '密码',
name varchar(10) not null comment '姓名',
gender tinyint unsigned not null comment '性别, 说明: 1 男, 2 女',
image varchar(300) comment '图像',
job tinyint unsigned comment '职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管',
entrydate date comment '入职时间',
dept_id int unsigned comment '部门ID',
create_time datetime not null comment '创建时间',
update_time datetime not null comment '修改时间',
constraint emp_dept foreign key(dept_id) references dept(id)
) comment '员工表';
这样就在建表的时候创建了一个外键约束,通过员工表中的字段dept_id和部门表中的id进行关联,将dept表中的id作为员工表的外键,使用约束在物理上进行关联,使得数据能够保证一致性和完整性。
使用物理外键存在的问题
使用foreign key外键可以使得两张表产生联系,但是因为是物理上的约束,所以说也有一些缺点:1.影响数据库CRUD的效率,因为每次在CRUD之前都需要检查外键的关系;2.foreign key外键只能用于单节点数据库,不适用于分布式、集群的场景;3.容易引发数据库的死锁问题,消耗性能。现在已经有很多项目明文禁止使用物理外键,现在更加推荐使用逻辑外键的方式解决表的关联问题:在业务层的逻辑中,解决外键关联,主要是通过代码来保证数据的一致性的,通过逻辑外键也可以保证数据的一致性,并且不会存在物理外键的问题,是推荐的做法。
一对一
一对一的关系,常用于多表拆分,将一张表的一些字段放在一张表中,其他字段放在另一张表中,进行分表,可以提高操作效率。一对一关系实现:在一方的字段中添加外键,关联另外一方的主键。
案例分析
比如说用户和其对应的身份证信息关系,可以在一张用户表中存放这两个信息(用户的基本信息和对应身份证信息),但是这种做法是不推荐的:将太多的信息全部放在一张表中,会降低操作效率。所以说一般会进行分表:将用户的基本信息分为一张表,将用户的身份证信息分为另外一张表,在身份信息表中用字段user_id作为外键关联用户信息表的主键,实现两张表的关联和一对一的关系(一个用户只能对应一张身份证)
在user_card表中添加外键user_id,关联user表的id(主键),并且设置外键为唯一,从而完成一对一的多表关系。
多对多
在一些案例中我们需要实现多对多的关系,比如说学生和课程的关系:一个学生可以选择多门课程,一门课程也可以供多个学生进行选择。这就会涉及到多对多的关系,那么这时单纯只依靠两张表之间就无法实现这样的关系了,就需要建立第三张中间表,中间表中至少包含两个外键,分别关联两方的主键:
create table tb_student(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '姓名',
no varchar(10) comment '学号'
) comment '学生表';
create table tb_course(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '课程名称'
) comment '课程表';
create table tb_student_course(
id int auto_increment comment '主键' primary key,
student_id int not null comment '学生ID',
course_id int not null comment '课程ID',
constraint fk_courseid foreign key (course_id) references tb_course (id),
constraint fk_studentid foreign key (student_id) references tb_student (id)
)comment '学生课程中间表';
我们先创建了两张表,分别是学生表和课程表,然后再创建了第三张中间表:学生课程中间表,其中有两个外键:student_id和 course_id分别关联学生id和课程id,这样就通过建立第三张中间表的方式,成功使得两张表实现了多对多的关系: