范式:规范
设计关系数据库时,要遵从不同的规范要求,设计出合理的表,这些不同的规范要求被称为不同的范式,就是前人总结出的良好数据库设计的经验。
目前关系型数据库有六种常见范式,按照范式级别,从低到高分别是:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯科德范式(BCNF)、第四范式(4NF)、第五范式(5NF,又称完美范式);
数据库的范式设计越高阶,数据的冗余度就越低,同时高阶的范式一定符合低阶范式的要求,满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),以次类推。一般来说,在关系型数据库设计中,最高也就遵循到BCNF,我们普遍用到的还是3NF
我们主要讲第一范式(1NF) 第二范式(2NF) 第三范式(3NF)
第一范式(1NF)的要求是属性不可分割,第二范式(2NF)的要求是满足第一范式的基础上,不存在部分依赖;第三范式(3NF)的要求是满足第二范式的基础上不存在传递依赖。
范式的作用
用来解决数据冗余和增加,删除,修改的异常,要判断一个数据库设计的是否合理,是否存在问题就从这两点判断。
不遵循范式出现冗余的例子
下面这张表存在什么问题,学生信息表
1.系名和系主任名相同的数据在这三条记录中重复出现,数据出现冗余
2.更新异常
这是学生信息表,但是在某系更换系主任后,比如王三改成李四,系统必须修改与该系学生有关的每一个行数据
3.插入异常
该插的数据插不进去,如果一个系刚成立,没有学生,就无法把这个系及其系主任的信息存入数据库。比如计科刚成立,没学生,就没办法保存系主任信息
4.删除异常 不该删除的数据不得不删
如果某个系的学生全部毕业了,比如小明,小张,小李毕业了,在删除该系学生信息的同时,把这个系及其系主任的信息也丢掉了。
不考虑范式设计,将信息全部存储在一张表里,如下表就会出现数据冗余,增,删,改,异常。部门刚成立,没有员工,无法插入部门信息,部门没有员工了,要删掉部门名称
好的关系的特点
不发生插入异常、删除异常、更新异常,数据冗余应尽可能少
不良关系的原因:由于存在于关系中的某些数据依赖引起的
解决方法:通过分解关系(表),多建立几张表,来消除其中不合适的数据依赖。
1.第一范式(1NF)
保持列的原子性
第一范式是指数据库表中的每个字段都是原子性的,即不可再拆分。
例1,假设我们有一个学生表,其中包含学生的姓名、电话和学校所在省县。省县不满足原子性。不拆分的话
如果有一天这个县划归到另一个省,那么相关记录都得改。
如果有一天这个县改名,那么相关记录都得改
如果有一天这个省改名,那么相关记录都得改
按第一范式,原子性的要求,应该将学校所在省县拆分分学校所在省和学校所在县两列
注意:有些人说姓名不满足原子性,能拆成姓氏,和名。不要较真,根据业务需求来拆分
如果你的系统里物流信息需要按省市县划分,那么右边的详细地址就不满足原子性,根据业务来判断
例2,个人信息字段包含手机号职务 所以不满足第一范式
可以拆分成手机号和职务,可调整如下
例子3 进货,销售字段 不满足第一范式
例子4
“家庭信息”和“学校信息”列均不满足原子性的要求,故不满足第一范式,需将这两列进行拆分,调整后,每一列都是不可再分的,调整如下:
例子5 电话不满足原子性
总结 第一范式是指数据库表中的每个字段都是原子性的,不可再拆分
2.第二范式(2NF)
目的就是消除部分依赖,针对联合主键(两个及以上字段)
在满足1NF的前提下,表中不存在部分依赖,非主键列要完全依赖于主键。(主要是说在联合主键的情况下,非主键列不能只依赖于主键的一部分)
说人话就是:每张表只描述一件事情
第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言),
如果有哪些数据只和主键的一部分有关的话,就得把它们独立出来变成另一个数据表。注意:第二范式是针对联合主键说的,如果一个数据表的主键只有单一一个字段的话,它就一定符合第二范式。
例子1
表中主键为“学生ID”和“课程ID”组成的联合主键(主键是可以有多个)。“学生ID”和“课程ID”两个值才能决定“得分”的值;而“课程名称”只依赖于“课程ID”,与“学生ID”没有依赖关系,它不完全依赖于主键,只依赖于主键的一部分,不符合2NF。
修改使表满足2NF后:将原来的成绩表(score)拆分为成绩表(score)和课程表(kc),而且两个表都符合2NF。
例子2
在选课表中,同学们可以一次选择多门课程,因此主键是“选课编号”和“课程编号”的联合主键,但是我们发现“课程名称”和“授课老师”两个字段,只与“课程编号”相关
也就是说不满足2nf 的规则 表中存在部分依赖,非主键列并没有按照2NF要求完全依赖于主键,一张表描述了多件事。
如果不拆分会出现什么问题,授课老师和课程名称发生变化,会去修改表中所有相关信息
所以应该放到“课程信息表”中,而不是“学生选课表”中。授课老师和课程名发生变化,不用去修改选课表中所有相关信息
表中不存在部分依赖,非主键列要完全依赖于主键
例子3 要设计一个订单信息表,因为订单中可能会有多种商品,所以要将订单编号和商品编号作为数据库表的联合主键
这样就产生一个问题:这个表中是以订单编号和商品编号作为联合主键。这样在该表中商品名称、单位、商品价格等信息不与该表的联合主键相关,而仅仅是与商品编号相关。所以在这里违反了第二范式的设计原则(表中不存在部分依赖,非主键列要完全依赖于主键)。
把这个订单信息表进行拆分,把商品信息分离到另一个表中,把订单项目表也分离到另一个表中 ,拆分后
例子4
同一个组件有可能由不同的供应商提供,所以得把组件 ID 和供应商 ID 合在一起组成一个主键。 但是供应商的名称和住址就只和供应商 ID 有关(部分依赖),这不符合第二范式的原则(表中不存在部分依赖,非主键列要完全依赖于主键)。
仔细看就会发现 "Nick" 这个名称和 "VA" 这个住址重复出现了两次;要是它改名了或是被其他公司并购了怎么办?
这时候最好把这些数据存到第二个数据表中,修改如下:
例子5
成绩取决于学号,课程号两个联合主键,学分只于课程号有关联,存在部分依赖
总结 2NF在1NF的基础之上,消除了非主属性对于主键(联合主键)的部分依赖。注意:只有一个主键的情况下,肯定满足2NF
3.第三范式(3NF)
消除传递依赖
第三范式是在满足第二范式的基础上,消除非主键字段之间的传递关系。它要求每个非主键字段只依赖于主键,而不依赖于其他非主键字段。也就是说非主键值不依赖于另一个非主键值。
就是说数据不能存在传递关系,即每个属性都跟主键有直接关系而不是间接关系。像:a-->b-->c 属性之间含有这样的关系,是不符合第三范式的。
比如Student表(学号,姓名,年龄,性别,所在院校,院校地址,院校电话)
这样一个表结构,就存在传递关系。
学号--> 所在院校 --> (院校地址,院校电话)
拆分如下。
(学号,姓名,年龄,性别,所在院校)--(所在院校,院校地址,院校电话)
例1,假设我们有一个员工表,其中包含员工ID、员工姓名、所属部门和部门负责人。
以上表既满足第一范式也满足第二范式,非主键字段也完全依赖于主键字段。
但是,部门负责人字段,其实是依赖所属部门字段的。也就是说,部门负责人字段是非主键值,而依赖了另一个非主键值-所属部门。所以就不符合第三范式.(3NF要求每个非主键字段只依赖于主键,而不依赖于其他非主键字段)。
在第三范式下,我们应该将所属部门和部门负责人拆分为独立的表,以避免员工表中的冗余数据,并确保每个非主键字段只依赖于员工号。
修改表使之满足3NF后:
例子2
以上表既满足第一范式也满足第二范式,非主键字段也完全依赖于主键字段。
但是,院系电话字段,其实是依赖院系字段的。也就是说,院系电话字段是非主键值,而依赖了另一个非主键值-院系。所以就不符合第三范式(3NF要求每个非主键字段只依赖于主键,而不依赖于其他非主键字段)。
拆分后:
举例3
产品名称依赖于非主键产品ID,不依赖于ID(3NF要求每个非主键字段只依赖于主键,而不依赖于其他非主键字段)。
举例4
表中存在一个传递依赖,(学号)->(班级)->(班主任)。也就是说,(班主任)这个非主键列依赖与另外一个非主键列 (班级)。所以不符号第三范式。(3NF要求每个非主键字段只依赖于主键,而不依赖于其他非主键字段)。
把这个表拆分成如下2个表
这样,对主键的传递依赖就消失了。上面的2个表都符合第3范式。
举例5 最高/最低月薪依赖于职位,不依赖于工号。不满足3NF(要求每个非主键字段只依赖于主键,而不依赖于其他非主键字段)。
4.bcnf范式
尽管BCNF和第三范式(3NF)有很多相似之处,但BCNF比3NF更为严格。3NF要求每一个非主属性必须完全依赖于主键,或依赖于候选键的一个非主属性。 BCNF则要求每一个非平凡的函数依赖关系的左边必须是一个超键,这意味着BCNF消除了所有非主属性对非候选键的依赖关系。换句话说,所有的函数依赖关系在BCNF中都必须依赖于一个超键。这种更严格的要求使得BCNF能够进一步减少数据冗余和异常情况。
为了更好地理解BCNF,以下是一个实际案例分析。假设我们有一个学生选课数据库,其中包括学生ID、课程ID和讲师ID三个属性。我们可以定义以下函数依赖关系:
- 学生ID -> 课程ID
- 课程ID -> 讲师ID
在这个例子中,学生ID不是一个超键,因为它不能唯一标识每一行。为了使数据库满足BCNF,我们需要将其分解成两个表:
- 学生课程表(学生ID,课程ID)
- 课程讲师表(课程ID,讲师ID)
通过这种分解,我们确保了每个表都满足BCNF的要求,从而减少了数据冗余,提高了数据的一致性和完整性。
假设仓库管理关系表(仓库号,存储物品号,管理员号,数量),满足一个管理员只在一个仓库工作;一个仓库可以存储多种物品,则存在如下关系:
(仓库号,存储物品号)——>(管理员号,数量)
(管理员号,存储物品号)——>(仓库号,数量)
所以,(仓库号,存储物品号)和(管理员号,存储物品号)都是仓库管理关系表的候选码,表中唯一非关键字段为数量,它是符合第三范式的。但是,由于存在如下决定关系:
(仓库号)——>(管理员号)
(管理员号)——>(仓库号)
即存在关键字段决定关键字段的情况,因此其不符合BCNF。把仓库管理关系表分解为两个关系表仓库管理表(仓库号,管理员号)和仓库表(仓库号,存储物品号,数量),这样这个数据库表是符合BCNF的,并消除了删除异常、插入异常和更新异常。
四、第四范式的优点和缺点
第四范式的主要优点包括:减少数据冗余、提高数据一致性、简化数据维护。由于消除了多值依赖,数据冗余得以减少,数据的一致性和完整性得以提高。例如,在更新数据时,只需要更新一个表中的数据,而不需要同步更新多个表。这大大简化了数据的维护工作。
然而,第四范式也有一些缺点:增加了表的数量、复杂了查询操作。由于将一个表拆分成多个表,表的数量增加了,查询操作也变得更加复杂。例如,为了获取一个学生的所有信息,需要进行多次表连接操作,这会增加查询的复杂性和开销。
5.第四范式(4NF)
第一范式要求所有的字段都是原子的,即每个字段只能包含一个值。第二范式在第一范式的基础上,要求消除部分依赖,即非键属性必须完全依赖于主键。第三范式要求消除传递依赖,即非键属性不能依赖于其他非键属性。BCNF是第三范式的强化版本,要求每个非键属性都完全依赖于候选键。第四范式则在这些基础上进一步消除多值依赖。
多值依赖的定义和例子
多值依赖是指在一个关系表中,存在一个非键属性集合的值依赖于另一个非键属性集合的值,而不是主键。例如,考虑一个表格,其中存储了学生、课程和兴趣爱好。如果一个学生可以选修多门课程,同时也可以有多个兴趣爱好,这样的表格就会存在多值依赖。
假设一个学生可以选修多门课程,同时也可以有多个兴趣爱好,这种情况下,一个学生的课程和兴趣爱好之间是独立的。如果我们将这些信息存储在一个表中,会导致大量的冗余数据。例如:
在上面的表格中,学生1选修了数学和英语,并且有足球和篮球两个兴趣爱好。这个表格存在多值依赖,因为"课程"和"兴趣爱好"是独立的,但它们都依赖于学生ID。这种设计会导致数据冗余和更新异常。
第四范式的要求
为了消除多值依赖,表格需要满足第四范式的要求。第四范式要求一个表格中不应存在多值依赖,即一个属性的值不应依赖于另一个非键属性的值。换句话说,每个非键属性必须直接依赖于主键,而不能依赖于其他非键属性。
要达到第四范式,需要将具有多值依赖的表拆分成多个表,使得每个表只包含一个非键属性集合。例如,上述例子中的表格可以拆分成两个表:
通过这种拆分,我们消除了多值依赖,减少了数据冗余,同时提高了数据的一致性和完整性。
总结
关于数据表的设计,有三个范式需要遵循:
第一范式:
数据库的每一列都是不可分割的原子数据项,不可再分的最小数据单元,而不能是集合、数组、多条记录等非原子数据项。
第二范式:
确保每列属性都和主键完全依赖,在联合主键的情况下,非主键部分不应该依赖于部分主键。每张表只描述一件事
第三范式:
确保每列都和主键列直接相关,不依赖于非主键列,而不是间接相关,不存在,传递依赖
扩充:人们常说要遵循数据库方式,教材讲义里也是。但是实际工作中,为了提高查询速度,需要反范式设计。
反范式设计是一种与传统规范化设计相对的数据库设计方法,它允许在数据库中引入冗余数据以提高查询性能或简化查询操作。
反范式设计的主要思想是通过增加冗余数据来消除关系型数据库中的连接操作,从而提高查询性能。
大数据技术里面有时用到反范式 比如elasticsearch
如大型数据仓库、报表生成和实时大数据处理等情况。然而,需要注意的是,反范式设计也带来了数据冗余和更新复杂性的问题,需要在权衡性能和数据一致性之间进行合理的选择。