(一).表的设计
在设计表之前,我们需要从需求中获得实体,实体就是在数据库中的一张张的表,实体的属性就是表中的字段(列),然后确定实体与实体之间的关系,然后使用SQL去创建具体的表。
(二).三大范式
在设计表的时候会尊少一些规则,这些规则被称为三大范式。
范式描述的就是数据关系的模型 ,分别为 "一对一关系","一对多关系","多对多关系"
范式的分类 :第一范式,第二范式,第三范式,BC范式,这里重点讲解第一范式,第二范式,第三范式
1.第一范式
表中的字段不可再进行拆分
第一范式是关系型数据库的一个最基本的要求,如果不满足第一范式就不能被称为关系型数据库。
示例:
描述一个学生的信息
反例
sql
create table if not exists student2(
id bigint primary key auto_increment,
name varchar(20) not null,
age int,
shcool varchar(50)
);
上面SQL代码是我创建的一个 "学生"表的代码,包含了编号、名字、年龄和学校
此时这个 "学生"表就不满足第一范式,因为,没有一个数据类型用来表示学校,学校还可以分为 "学校名称","学校地址","学校电话"
正例
sql
create table if not exists student1(
id bigint primary key auto_increment,
name varchar(20) not null,
age int,
school_name varchar(20),
school_address varchar(50),
school_tele varchar(20)
);
这个 "学生"表就满足第一范式了,因为我们将 "学校"拆分开了
虽然这样设计表不符合数据库设计的规范,但是每一列都是不可再分的,最起码可以表明一个学生和班级以及学校之间的关系。
注意:在定义表的时候,如果每个字段都可以用一个数据类型表示,那么当前这个表就天然满足第一范式
2.第二范式
在满足第一范式和存在复合主键的基础上,不存在非关键字段对任意候选键的部分函数依赖
非关键字段:非主键字段(下面例子中的 "age" "name")
候选键:主键,外键,没有主键时的唯一键(下面例子中的id)
复合主键:一个表中不能存在两个主键,但是一个主键可以包含多个列
示例:
学生选公开课,公开课都有相应的学分,学生考试之后会生成相应的考试成绩
反例:
sql
create table student3(
id bigint primary key auto_increment,
name varchar(20) not null,
age int,
class varchar(30),
credit int,
score decimal(5,2)
);

其实,我们可以发现,"name"字段和 "age"字段可以通过 "id" 字段来确定,"credit"字段可以通过"class"字段来确定,"score"字段是通过"id"字段和"class"字段共同来确认。
也就是说,这个"成绩表"可以通过 "学生id" 和 "class"为复合主键来确定。
学生的"name"字段和"age"字段与 "class"字段并没有关系,它俩只依赖于学号,不依赖于课程。同时,"credit"字段和学生没关系,他只依赖于 "class"字段,不依赖于学生。
对于由两个或者多个关键字段决定一条记录的情况,如果一行数据中有些字段只与关键字段中的一个有关系,例如:"age"字段和"name"字段只与学生的id有关系,那么就说这个表存在函数依赖,那么就不满足第二范式。
正例:
可以设计三个表:学生表,课程表,成绩表
sql
-- 学生表
create table student4(
id bigint primary key auto_increment,
name varchar(50) not null,
age int
);
-- 课程表
create table class(
id bigint primary key auto_increment,
name varchar(50),
credit int
);
-- 成绩表
create table score(
id bigint primary key auto_increment,
student_id bigint,
class_id bigint,
score decimal(5,2),
-- 通过外键建立关系
foreign key (student_id) references student4(id),
foreign key (class_id) references class(id)
);
这样设计表,每张表都有非主键的字段,都依赖于主键,满足第二范式
如果一张表中没有复合主键,只有主键一列,那么这个表就天然满足第二范式
不满足第二范式会出现的问题

就拿这张表说吧
(1).数据冗余
"name"字段,"age"字段 和 "credit"字段,都重复出现,造成了大量数据的冗余
(2).更新异常
当我要修改MYSQL的学分,就需要更新所有记录中关于MYSQL的记录,如果某些更新成功,某些更新失败,就会造成数据表中的同一门课的不同学分的情况,从而造成数据不一致
(3).插入异常
每一门课都与学生的考试成绩是对应关系,只有同学考试之后才会生成关于这门课的成绩记录,也就是说,在学生未考试之前是没有相应的记录的,因为学生成绩为空是没有意义的
(4).删除异常
当删除记录的时候,课程的学分也会随之删除,这样会导致数据库中没有课程和学分的相应信息
3.第三范式
在第二范式的基础上,不存在非关键字段,对任意候选键的传递依赖
示例:
描述学生就读的学院
反例
sql
create table student5(
id bigint primary key auto_increment,
name varchar(20),
age int,
college varchar(20),
college_address varchar(20),
college_tele varchar(20)
);
这个表本质上就记录了学生和学院这两个实体
由于描述的是学生,所以id作为主键
在这个表中,"id"字段和 "name"字段与 "age"字段是强相关的;"college"字段和"college_address"与 "college_tele"字段是强相关的
所以,在这个表中,出现两种强相关关系,同时这两种强相关关系存在传递现象
通过学号可以找到就读的学院,通过就读的学院就可以找到学院的地址和学院的电话
这种传递关系被称为传递依赖
因此这种设计不满足第三范式,原因是存在传递依赖
其实,想要描述清除学生所在的学院,只需要把学生和学院建立一个关联关系即可
正例:
创建两个表,一个学院表,一个学生表
sql
-- 创建学院表
create table college(
id bigint primary key auto_increment,
name varchar(20),
address varchar(20),
tele varchar(20)
);
-- 创建学生表
create table student6(
id bigint primary key auto_increment,
name varchar(20),
age int,
college_id bigint,
foreign key (college_id) references college(id) --通过外键连接
);
这样的设计,两张表都依赖于自己表中的主键,学生表都可以通过外键与学院表建立关联关系
(三).实体之间的关系
1.一对一
一对一关系,一般都会存在两个实体
针对一对一的关系,有两种方式来设计表
方式1:将两个实体所有的信息都集中到一张表中
方式2:创建两张表,分别记录两个实体的信息,并把这两张表做关联
示例:
一个人只有一个身份证号
方式一:将人的信息和身份证上的信息集中到一张表中
sql
create table people1(
id bigint primary key auto_increment,
name varchar(20),
age int,
idNumber bigint comment '身份证号',
date_of_Birth timestamp comment '出生日期'
);
方式二:创建两张表,分别记录两个实体的信息,并把这两张表做关联
sql
-- 创建身份证表
create table ID_card(
idNumber bigint primary key comment '身份证号',
name varchar(20) comment '姓名',
date_of_Birth timestamp comment '出生日期'
);
-- 创建人表
create table people(
id bigint primary key auto_increment,
name varchar(20),
age int,
id_card_number bigint,
-- 关联方法1:在people中使用外键关联ID_card中的idNumber
foreign key (id_card_number) references ID_card(idNumber)
);
sql
-- 创建身份证表
create table ID_card(
idNumber bigint primary key comment '身份证号',
name varchar(20) comment '姓名',
date_of_Birth timestamp comment '出生日期'
-- 关联方法2:在ID_card中使用外键关联people中的id
foreign key (idNumber) references people(id);
);
-- 创建人表
create table people(
id bigint primary key auto_increment,
name varchar(20),
age int,
id_card_number bigint,
);
两种关联方法选一个即可
2.一对多
示例:
学生和班级之间关系,一个学生只能存在于一个班级,而一个班级可以有很多学生
在学生的角度是多对一的关系,在班级的角度是一对多的关系
我们可以创建两个表,一个是学生表,一个是班级表,然后在学生表中使用外键和班级表创建关联关系,即通过学生表中的class_id就可以表示学生在哪个班级
sql
-- 创建班级表
create table class(
id bigint primary key auto_increment,
name varchar(20)
);
-- 创建学生表
create table student7(
id bigint primary key auto_increment,
name varchar(20),
class_id bigint,
foreign key (class_id) references class(id) -- 通过外键建立联系
);
3.多对多
示例:
学生选课程
一个学生可以选择多门课程
同样,一门课程也可以被多名学生选择
我们需要创建三个表,分别是 "学生表","课程表","关系表","学生和课程之间的关系"表
sql
-- 创建学生表
create table student8(
id bigint primary key auto_increment,
name varchar(20)
);
-- 创建课程表
create table course(
id bigint primary key auto_increment,
name varchar(20),
credit int
);
-- 创建关系表
create table student_course(
id bigint primary key auto_increment,
student_id bigint,
course_id bigint,
foreign key (student_id) references student8(id),
foreign key (course_id) references course(id)
);

4.没有关系
没有关系的表,我们直接创建即可,不需要考虑其他的关系