数据冗余
我们在设计数据库时如果如果不按照一定的规范去设计很容易会出现数据冗余,即有多余的数据重复重现,浪费内存。
例如:学生-老师的表设计如下
sno | sname | tno | tname | t_tel |
---|---|---|---|---|
001 | 李四 | 002 | 黄老师 | 138****90 |
002 | 王五 | 001 | 张老师 | 137****23 |
003 | 赵柳 | 002 | 黄老师 | 138****90 |
004 | 宋毅 | 001 | 张老师 | 137****23 |
如果这样设计,因为一个老师对应很多个学生,所以老师的id和他的相关信息就要重复存很多次,造成资源浪费。
为了解决这个问题,可以使用三范式(3NF),它是设计表的依据,按照这个三范式设计的表不会出现数据冗余。
第一范式:任何一张表都应该有主键,并且每一个字段原子性不可再分
首先是主键,主键是表的行的唯一标识的关键字。举个例子,如下面的表
id | name | hobby | tel |
---|---|---|---|
001 | 李四 | 游泳 | 138****90 |
002 | 王五 | 跑步 | 137****23 |
001 | 赵柳 | 跑步 | 138****90 |
002 | 宋毅 | 漫画 | 137****23 |
id就是这张表的主键,001可以标识第一行的数据。通俗点说就是,id是001的只有李四,然后可以根据id为001来选择我们所需的name,hobby或tel的信息。但是如果用habit做主键,并不能唯一标识这一行的数据,如hobby为跑步的,不仅仅是王五,赵柳也是,所以如果hobby不能是主键,因为它没办法标识某一行。姓名也不行,因为会有重名的存在。
主键一般是一个,也有多个的复合主键。
其次是每一个字段原子性不可再分,就是每一列都是不可再分的,例如:
id | name | hobby | 联系方式 |
---|---|---|---|
001 | 李四 | 游泳 | 138*90,30222@qq.com |
002 | 王五 | 跑步 | 137*23,30222@qq.com |
001 | 赵柳 | 跑步 | 138*90,30222@qq.com |
002 | 宋毅 | 漫画 | 137*23,30222@qq.com |
这个样子就不符合原子性,因为联系方式还可以划分为电话和邮箱。但是不要钻牛角尖(因为当时我就是)认为,姓名也可以拆分呀,拆成姓和名。虽然是可以,但是没有必要,意义不大。
表的设计满足有主键,并且每一列需要符合原子性,则称为满足第一范式。
第二范式:建立在第一范式的基础之上,所有非主键字段完全依赖主键,不能产生部分依赖
学生编号(PK) | 教师编号(PK) | 学生姓名 | 教师姓名 |
---|---|---|---|
1001 | 001 | 张三 | 王老师 |
1002 | 002 | 李四 | 赵老师 |
1003 | 001 | 王五 | 王老师 |
以上虽然确定了主键,但此表会出现大量的冗余,主要涉及到的冗余字段为“学生姓名”和“教师姓名”,出现冗余的原因在于,学生姓名部分依赖了主键的一个字段学生编号,而没有依赖教师编号,而教师姓名部门依赖了主键的一个字段教师编号,这就是第二范式部分依赖。
解决方法:将表格拆分为两张基础表和一张练习表
学生编号(PK) | 学生姓名 |
---|---|
1001 | 张三 |
1002 | 李四 |
1003 | 王五 |
教师编号(PK) | 教师姓名 |
---|---|
001 | 王老师 |
002 | 赵老师 |
001 |
关系表
学生编号(PK) fk学生表的学生编号 | 教师编号(PK) fk教师表的教师编号 |
---|---|
1001 | 001 |
1002 | 002 |
1003 | 001 |
1001 | 002 |
第三范式:建立在第二范式的基础之上,所有非主键字段直接依赖主键,不能产生传递依赖
学生编号(PK) | 学生姓名 | 班级编号 | 班级名称 |
---|---|---|---|
1001 | 张三 | 01 | 一年一班 |
1002 | 李四 | 02 | 一年二班 |
1003 | 王五 | 03 | 一年三班 |
1004 | 赵六 | 03 | 一年三班 |
从上表可以看出,班级名称字段存在冗余,因为班级名称字段没有直接依赖于主键,班级名称字段依赖于班级编号,班级编号依赖于学生编号,那么这就是传递依赖。
解决的办法是将冗余字段单独拿出来建立表,如
学生信息表
学生编号(PK) | 学生姓名 | 班级编号(FK) |
---|---|---|
1001 | 张三 | 01 |
1002 | 李四 | 02 |
1003 | 王五 | 03 |
1004 | 赵六 | 03 |
班级信息表
班级编号(PK) | 班级名称 |
---|---|
01 | 一年一班 |
02 | 一年二班 |
03 | 一年三班 |
三范式总结
第一范式:有主键,具有原子性,字段不可分割
第二范式:完全依赖,没有部分依赖
第三范式:没有传递依赖
数据库设计尽量遵循三范式,但是还是根据实际情况进行取舍,有时可能会拿冗余换速度,最终用目的要满足客户需求。
一对一:主键共享和外键唯一
- 主键共享
t_user_login 用户登录表
id(pk) | username | password |
---|---|---|
1 | zs | 123 |
2 | ls | 456 |
t_user_detail 用户详细信息表
id(pk+fk) | realname | tel |
---|---|---|
1 | 张三 | 138***90 |
2 | 李四 | 168***44 |
- 外键唯一
t_user_login 用户登录表
id(pk) | username | password |
---|---|---|
1 | zs | 123 |
2 | ls | 456 |
id(pk+fk) | realname | tel | userid(fk+unique) |
---|---|---|---|
1 | 张三 | 138***90 | 1 |
2 | 李四 | 168***44 | 2 |
一对多
如班级和学生就是一对多的情况,设计思路是两张基础表,多的加外键
班级t_class
cno(pk) | cname |
---|---|
1 | 班级一 |
2 | 班级二 |
学生t_student
sno(pk) | sname | classno(fk) |
---|---|---|
101 | 张1 | 1 |
102 | 张2 | 1 |
103 | 张3 | 2 |
104 | 张4 | 2 |
105 | 张5 | 2 |
PS:在实际的开发中,以满足客户的需求为主,有的时候会拿冗余换执行速度。
多对多
两张基础表,一张关系表,两个外键
t_student学生表
sno(pk) | sname |
---|---|
1 | 张三 |
2 | 李四 |
3 | 王五 |
t_teacher 讲师表
tno(pk) | tname |
---|---|
1 | 王老师 |
2 | 张老师 |
3 | 李老师 |
t_student_teacher_relation 学生讲师关系表
id(pk) | sno(fk) | tno(fk) |
---|---|---|
1 | 1 | 3 |
2 | 1 | 1 |
3 | 2 | 2 |
4 | 2 | 3 |
5 | 3 | 1 |
6 | 3 | 3 |