数据库有三大范式和BC范式,我们来详细探讨一下:
首先三大范式:
第一范式
第一范式(1NF):表中所有属性都不能再分,都应该是原子值。
这也是数据表的最低的最基本的要求。
第二范式
第二范式(2NF):在满足第一范式的前提下,还要求每一个非主属性都要完全依赖于任何一个候选码。
上面这句话听起来比较抽象,其实也好理解。(下面的主键(限第二范式中)其实指:主键或候选码,这么写的看起来太绕就简写成主键了,大家注意一下)
它的意思就是指,每一个非主属性都应该和主键有关(不能只和主键的一部分相关,这是指联合主键),即一个表只做一件事。
举个例子,下面是一个关系,
Student(Sno, Sdept, Sloc, Cno, Grade)
- Sno:学号
- Sdept:学生所在的系
- Sloc:学生的住处,每个系的学生住在一起
- Cno:课程编号
- Grade:该学生该课程的成绩
其中该关系的主键为(Sno, Cno)
显然该关系就不符合第二范式,而这样会出现很多的问题。
(1)插入异常
如果要插入一个学生,但是该学生还未选课,那么他就没有Cno,而主键又不能为空,那么他就插不进去…
(2)删除异常
假设一个学生本来只选了一门课,现在他一门课都不想选了,想把这门课退掉,那么这个学生的所有信息也会随之而被删掉…
(3)修改复杂
如果一个学生转系了,那么修改起来就很麻烦,冗余数据很多。本来只要改Sdept就行了,但是现在还需要对它的Sloc进行修改,而且如果他选了5门课,则需要修改Sdept
和Sloc
五次。
所以应该将其分成两个表:
CJ(Sno,Cno,Grade) /*成绩表*/
StudentInfo(Sno,Sdept,Sloc) /*学生基础信息表*/
第三范式
第三范式(3NF):在满足第二范式的前提下,还要求表中不存在传递依赖,即表中每一列都要与主键直接相关,而非间接相关。
举个栗子:
订单表(订单号, 订购日期, 顾客编号, 顾客名)
上面表中,显然订单号是主键,而顾客名与订单号却是间接相关的,他们中间通过顾客编号相连。
这显然是不满足第三范式的,因此需要将其进行修改:
订单表(订单号,订购日期,顾客编号)
顾客表(顾客编号,顾客名)
这样显然也是一种避免冗余的手段。
如何区分这三种范式呢?
第一范式:表中的每一列都是最小的单元,任何一列都不能再进行拆分了;
第二范式:表中所有的列都应该是和主键(的全体)完全相关的,即不能只和主键的某一部分相关;
第三范式:表中每一个非主属性都要和主键直接相关,即不要在表中出现太多另外一个实体的信息,只需要出现它的id就行了。
范式之所以叫做范式,就是因为它给数据库的设计提供了一种规范,能够最大可能的避免犯错,也减少了数据的冗余,提高数据库效率。
附加:BC范式(BCNF)
数据库的三大范式只是最基本的,而BC范式也常与他们放到一起讨论,因为BCNF也被称为修正的第三范式,又或者说是扩充的第三范式。
为什么叫修正的第三范式?那么表示第三范式肯定有所缺漏,那么缺漏是什么呢?又如何补救呢?
我们看到第三范式的要求是每一个非主属性都要直接依赖于主属性,看似完美,可是如果除了主属性外,还有一个候选码呢?
显然从定义可以知道,这个主属性肯定能和候选码一一对应的,那这样岂不是又会造成冗余?
觉得很抽象吗?举个例子:
仓库(仓库编号,货物编号,仓库管理员编号)
其中每一个仓库管理员只管理一个仓库。
那么我们可以发现这里其实主码可以有两种,分别是:
- (仓库编号) 可唯一确定 (仓库管理员编号,货物编号)
- (仓库管理员编号) 可唯一确定 (仓库编号,货物编号)
必须要承认上述关系是符合第三范式的吧,但是有没有觉得这样仓库管理员编号会出现大量的没必要的冗余啊,因此BC范式就是解决这个问题的,需要将其改为两个表,顺便可以将货物的数量加进来。
至于为什么上面关系中我不将货物数量加进来,是因为一旦加进来后那个关系就不符合第二范式了,想想看,如果加入货物数量,那么主键就变成了(仓库编号,货物编号)
,可是仓库管理员只与仓库编号有关,不依赖于货物编号了呀,就不构成对主键的完全依赖关系了。
下面放上BC范式的修改版:
仓库与管理员表(仓库编号,仓库管理员编号)
仓库货物表(仓库编号,货物编号,货物数量)
参考资料:
https://www.cnblogs.com/waj6511988/p/7027127.html
https://blog.csdn.net/javanotes/article/details/81368715
https://www.cnblogs.com/knowledgesea/p/3667395.html
https://blog.csdn.net/u014458048/article/details/56678698