数据库范式Normal Forms听起来很高端但它并不是什么高级玩意就是教你怎么把数据表设计得“干净、不拖泥带水”从而避免每次新增、修改或删除数据时引发一连串的错误也称之为“异常”。有一句非常经典的顺口溜来总结前三个最常用的范式“要求字段具有原子性且依赖于主键完全依赖于主键并且只依赖于主键。”首先先来一份总结表格范式条件解决的问题1NF每个属性都是不可再分的原子值消除表中重复组、多值属性、非原子属性2NF在 1NF 基础上非主属性完全函数依赖于候选码消除非主属性对候选码的部分函数依赖3NF在 2NF 基础上非主属性不传递依赖于候选码消除非主属性对候选码的传递函数依赖BCNF每个非平凡函数依赖X - Y中X 都必须是超码消除由非候选码决定其他属性造成的异常比 3NF 更严格4NF每个非平凡多值依赖X -- Y中X 都必须是超码消除不合理的多值依赖5NF每个非平凡连接依赖都由候选码蕴含消除不合理的连接依赖如果你能看懂那没事了可以退出本文了~1. 第一范式 (1NF)每个格子只能装一个东西表格原话每个属性都是不可再分的原子值。大白话表格里的每一个单元格只能放一个单一的数据不能放一个列表、数组或者多个值拼在一起的字符串。为什么要这样如果你把多个电话号码写在一个格子里你想按特定电话号码搜索或者修改其中一个号码时系统会非常痛苦。错误示范不符合 1NF学号姓名联系方式001张三手机: 138xxx, 邮箱: zabc.com正确示范符合 1NF学号姓名联系类型联系号码001张三手机138xxx001张三邮箱zabc.com2. 第二范式 (2NF)不要“只依赖一半”表格原话在 1NF 基础上消除非主属性对候选码的部分函数依赖。大白话这一条主要针对联合主键也就是用两个或以上的字段共同作为主键。如果你的表有两个主键比如“学号”“课程号”那么表里的其他信息必须同时和这两个主键都有关系不能只跟其中一个有关系。为什么要这样如果某些信息只和部分主键有关就会造成大量的数据冗余重复记录。错误示范不符合 2NF假设主键是(学号, 课程号)学号课程号成绩学生姓名001Math-0190张三001Eng-0285张三问题出在哪“成绩”确实是由学号课程号共同决定的但是“学生姓名”只跟“学号”有关跟“课程号”半毛钱关系都没有。这导致张三每选一门课名字就要重复存一遍。正确示范拆成两张表符合 2NF表1学生表(主键学号)学号学生姓名001张三表2成绩表(主键学号, 课程号)学号课程号成绩001Math-01903. 第三范式 (3NF)拒绝“间接依赖”表格原话在 2NF 基础上消除非主属性对候选码的传递函数依赖。大白话表里的非主键字段必须直接归主键管不能通过别人来管。也就是A - B - C是不允许的。为什么要这样避免修改数据时“牵一发而动全身”。错误示范不符合 3NF假设主键是员工工号员工工号员工姓名部门编号部门名称1001李四D01研发部1002王五D01研发部问题出在哪“部门名称”其实是依赖于“部门编号”的而“部门编号”依赖于“员工工号”。这就形成了传递依赖工号 - 部门编号 - 部门名称。如果研发部改名叫“技术部”你需要修改所有在 D01 部门的员工记录极易漏改。正确示范拆表符合 3NF将部门信息独立成表。员工表只保留部门编号作为外键。4. BCNF (巴斯-科德范式)——3NF 的加强版表格原话每个非平凡函数依赖X - Y中X 都必须是超码。大白话在 3NF 中我们规范了“非主键属性”不能乱来。但有时候主键本身内部也会互相打架。BCNF 规定只要有决定关系的字段组合谁能决定谁那个“决定者”就必须有资格做主键也就是超码。业务场景假设一个规定一个老师只教一门课但一门课可以有多个老师教。如果你把 (学生, 老师) 当作联合主键就会出现老师能反向决定课程的情况这就破坏了 BCNF。遇到这种情况依然是“拆表”解决。5. 第四范式 (4NF) 与 第五范式 (5NF)处理极其复杂的业务边缘情况在实际开发中做到 3NF 或 BCNF 已经能满足 99% 的业务需求了。4NF 和 5NF 更多是理论层面的完善用于解决特定的多对多关系。第四范式 (4NF) - 消除多值依赖大白话如果一个人有两个完全独立的多值属性不要把它们放在一张表里穷举组合。例子一个开发者既掌握多门语言Java, Python又有多个爱好游泳, 跑步。如果你硬把它们塞进一张“开发者能力爱好表”就会产生无意义的交叉组合Java游泳Java跑步Python游泳…。解决办法就是拆成“开发者-语言表”和“开发者-爱好表”。第五范式 (5NF) - 消除连接依赖大白话一张大表拆成三张小表后如果不丢失信息还能拼回原来的大表那就拆。这通常涉及到极其复杂的“三方联合业务”比如供应商、零件、项目三者之间互相都有多对多关系。