文章目录
前面写了一篇文章,链接如下: 数据库范式 ,佶屈聱牙,晦涩难懂,今天总结一篇通俗易懂的
范式目的
聊数据库范式,必须先讨论为何要增加数据库范式,数据库范式的目的主要是为了确保数据库设计的合理性和高效性,具体包括以下几个方面:
- 减少数据冗余:数据冗余是指数据在数据库中重复存储。通过范式的规则,如将数据按照不同的主题进行合理划分,存储在不同的表中,可以避免同一数据在多个地方出现。例如在第三范式中,要求消除非主键列之间的传递依赖,就是为了进一步减少数据冗余。以员工信息表为例,如果员工所属部门的信息(如部门名称、部门地址等)直接存储在员工表中,那么对于同一部门的员工,这些部门信息会重复存储。按照第三范式,将部门信息单独存储在部门表中,员工表只存储部门编号,通过部门编号关联部门表获取部门信息,这样就大大减少了数据冗余。
- 避免数据不一致:数据冗余可能导致数据不一致的问题,即同一数据在不同地方存储时,由于更新操作没有同步进行,导致数据出现差异。通过减少数据冗余,数据库范式有助于确保数据的一致性。例如,在上述员工信息表的例子中,如果部门地址发生变化,只需要在部门表中修改一次,而不会出现因为在多个员工记录中分别修改部门地址而导致的不一致情况。
- 提高数据的完整性:数据库范式要求数据满足一定的约束条件,从而保证数据的完整性。例如第一范式要求每列都是不可再分的基本数据项,这就确保了数据的原子性,使得数据在存储和处理时更加规范和准确。同时,通过外键约束等方式在不同表之间建立关联,也可以保证数据的参照完整性。比如在员工表和部门表的关联中,通过部门编号作为外键,确保员工所属部门的编号在部门表中存在,避免出现无效的部门编号,从而保证了数据的完整性。
- 增强数据库的可维护性和可扩展性:按照范式设计的数据库结构更加清晰,各个表的职责明确,数据之间的关系也更加规范。当需要对数据库进行修改、添加新功能或扩展数据结构时,更容易理解和操作。例如,如果要在系统中添加一个新的员工属性,只需要在员工表中添加相应的列即可,而不会对其他表造成过多的影响。相反,如果数据库设计没有遵循范式,可能会导致数据结构混乱,在进行维护和扩展时容易出现错误,增加开发和维护的成本。
- 优化数据库性能:虽然范式的主要目的不是直接提高性能,但合理的范式设计可以间接优化数据库性能。通过减少数据冗余和合理组织数据,可以减少磁盘I/O操作,提高查询效率。例如在查询员工信息时,如果员工表中没有冗余的部门信息,那么在查询员工基本信息时,所需读取的数据量就会减少,从而提高查询速度。同时,范式设计也有助于数据库系统更好地利用索引等优化技术,进一步提高性能。
第一范式(1NF)——数据拆到最小单位
核心要求:每一列都是不可再分的独立信息
错误示范:
学生ID | 借书记录 |
---|---|
1001 | 《三体》,2023-01-05; 《哈利波特》,2023-02-10 |
问题:借书记录
列里混合了书名和日期,还堆在一起用分号隔开。
改成1NF后:
学生ID | 书名 | 借书日期 |
---|---|---|
1001 | 《三体》 | 2023-01-05 |
1001 | 《哈利波特》 | 2023-02-10 |
关键点:每个单元格只存一个独立信息,不能再拆分。
第二范式(2NF)——消除重复的依赖
核心要求:所有列必须完全依赖主键(不能只依赖主键的一部分)
假设主键是(学生ID + 书名):
学生ID | 书名 | 借书日期 | 学生姓名 | 书的价格 |
---|---|---|---|---|
1001 | 《三体》 | 2023-01-05 | 张三 | 50 |
1001 | 《哈利波特》 | 2023-02-10 | 张三 | 60 |
问题:
学生姓名
只依赖学生ID(和书名无关)书的价格
只依赖书名(和学生ID无关)
改成2NF后拆成三张表:
-
学生表(主键:学生ID)
学生ID 学生姓名 1001 张三 -
书籍表(主键:书名)
书名 价格 《三体》 50 《哈利波特》 60 -
借阅记录表(主键:学生ID + 书名)
学生ID 书名 借书日期 1001 《三体》 2023-01-05 1001 《哈利波特》 2023-02-10
关键点:每个表只描述一件事(学生、书籍、借阅关系),避免信息重复存储。
第三范式(3NF)——消除间接依赖
核心要求:所有列必须直接依赖主键,不能通过其他列间接依赖
错误示范(书籍表):
书名 | 价格 | 出版社 | 出版社电话 |
---|---|---|---|
《三体》 | 50 | 重庆出版社 | 023-12345678 |
《哈利波特》 | 60 | 人民文学 | 010-87654321 |
问题:出版社电话
依赖的是出版社名称,而不是直接依赖书名(书名→出版社→电话)。
改成3NF后拆成两张表:
-
书籍表(主键:书名)
书名 价格 出版社 《三体》 50 重庆出版社 《哈利波特》 60 人民文学 -
出版社表(主键:出版社名称)
出版社 出版社电话 重庆出版社 023-12345678 人民文学 010-87654321
关键点:如果A→B→C(A决定B,B决定C),就需要拆表。
总结成一句话
- 一范式:列不可分 → 像整理抽屉,袜子不能和鞋子混放
- 二范式:消除“半个主键”的依赖 → 像厨房调料瓶,盐罐不能放糖的信息
- 三范式:消除“拐弯”依赖 → 像家族族谱,不能通过爸爸找爷爷的电话
实际应用:数据库设计一般到3NF就够用了,但有时为了查询更快,允许少量重复数据(比如电商订单页直接显示商品名称,而不是只存ID)。
其实范式总共有六个(1NF到5NF+BCNF),但实际开发中常用到第三范式(3NF),更高阶的范式通常出现在理论场景。
BCNF范式(修正的第三范式)——主键的绝对权威
核心要求:所有依赖关系的左侧必须是超键(即左侧能唯一确定整行数据)
典型问题场景:一个老师可以教多门课,但每门课只能由一个老师教
原始表(主键:学生ID + 课程):
学生ID | 课程 | 授课老师 |
---|---|---|
1001 | 数据库 | 王老师 |
1001 | 算法 | 李老师 |
1002 | 数据库 | 王老师 |
问题:
授课老师
只依赖课程(课程→老师),而主键是(学生ID+课程)- 左侧「课程」不是超键(无法唯一确定一行)
改为BCNF后拆成两张表:
-
课程-老师表(主键:课程)
课程 授课老师 数据库 王老师 算法 李老师 -
学生-课程表(主键:学生ID + 课程)
学生ID 课程 1001 数据库 1001 算法 1002 数据库
关键点:确保任何依赖关系中的“决定方”(如课程→老师)本身是主键或超键。
第四范式(4NF)——消灭多值依赖
核心要求:消除非平凡的多值依赖
典型问题场景:一个学生有多个兴趣爱好,同时选修多门课程
原始表(主键:学生ID):
学生ID | 兴趣爱好 | 选修课程 |
---|---|---|
1001 | 篮球, 摄影 | 数学, 物理 |
1002 | 唱歌 | 化学 |
问题:
- 兴趣爱好和选修课程是多值属性,且彼此独立
- 存储时会产生组合爆炸(比如1001的兴趣和课程需要排列组合)
改为4NF后拆成两张表:
-
学生-兴趣表
学生ID 兴趣爱好 1001 篮球 1001 摄影 1002 唱歌 -
学生-课程表
学生ID 选修课程 1001 数学 1001 物理 1002 化学
关键点:如果A→→B且A→→C,且B和C无关,就必须拆表。
第五范式(5NF)——终极拆分术
核心要求:表必须无法再拆分成更小的表(除非丢失信息)
典型问题场景:记录「课程-教师-教材」关系,且三者必须同时存在才有效
原始表:
课程 | 教师 | 教材 |
---|---|---|
数据库 | 王老师 | 《SQL指南》 |
数据库 | 王老师 | 《DB原理》 |
算法 | 李老师 | 《算法导论》 |
问题:
- 如果王老师换教材,需要修改多条记录
- 无法通过拆分两两关系表还原原始信息
改为5NF后拆成三张表:
-
课程-教师表
课程 教师 数据库 王老师 算法 李老师 -
课程-教材表
课程 教材 数据库 《SQL指南》 数据库 《DB原理》 算法 《算法导论》 -
教师-教材表
教师 教材 王老师 《SQL指南》 王老师 《DB原理》 李老师 《算法导论》
关键点:只有同时关联三者的数据才有意义,且拆分后能通过**连接(JOIN)**还原原表。
总结:范式就像俄罗斯套娃
- 1NF:拆分字段 → 拆散乐高零件
- 2NF:拆分表 → 把乐高按颜色分类
- 3NF/BCNF:再拆分 → 按形状二次分类
- 4NF:多维度独立分类 → 分装到不同盒子
- 5NF:终极分解 → 只有组合起来才能拼成完整模型
实际建议:
- 日常开发到3NF或BCNF足够
- 4NF和5NF多用于理论研究
- 过度范式化可能导致需要大量JOIN,反而降低性能!