1NF、2NF、3NF的区别,举例说明

在关系数据库设计中,1NF(第一范式)、2NF(第二范式)、3NF(第三范式)的核心目标是消除数据冗余避免操作异常(插入、删除、更新异常),它们的区别在于对“函数依赖”的约束强度逐级增强。以下从定义、核心要求、反例(不满足该范式的表)、正例(满足该范式的表)四个维度,结合同一业务场景详细说明:

基础概念铺垫

在举例前,先明确两个关键术语(后续案例会反复用到):

  • 函数依赖:若属性A的值能唯一确定属性B的值,则称“B依赖于A”(记为 A→B)。例如“学号→姓名”(一个学号对应唯一姓名)。
  • 候选码:能唯一标识一条记录的最小属性集(如“学号+课程号”可唯一标识一条选课记录);主属性是候选码包含的属性,非主属性是候选码外的属性(如“成绩”“姓名”)。

1. 第一范式(1NF):属性原子化,不可再分

核心要求

关系表中的每个属性必须是原子值(不可分割的最小单位),不能是集合、数组、嵌套结构等。
1NF是数据库设计的“入门要求”,不满足1NF的表甚至不能称为“关系表”。

反例(不满足1NF):学生选课表1

学号 姓名 选课信息(课程号+课程名+成绩)
2023001 张三 (001, 数学, 90), (002, 英语, 85)
2023002 李四 (001, 数学, 88), (003, 物理, 92)

问题:“选课信息”属性是“课程号+课程名+成绩”的集合,可再分,违反1NF。
导致的问题:无法单独查询“张三的数学成绩”,只能读取整个“选课信息”后拆分,操作效率极低。

正例(满足1NF):学生选课表2

学号 姓名 课程号 课程名 成绩
2023001 张三 001 数学 90
2023001 张三 002 英语 85
2023002 李四 001 数学 88
2023002 李四 003 物理 92

改进:将“选课信息”拆分为“课程号”“课程名”“成绩”三个原子属性,每个属性不可再分,满足1NF。

2. 第二范式(2NF):消除非主属性对候选码的“部分函数依赖”

核心要求

  1. 首先满足1NF;
  2. 消除非主属性对候选码的部分依赖(即非主属性必须依赖于候选码的“全部”,而不是“部分”)。

关键判断:若候选码是“单一属性”(如“学号”),则天然满足2NF(因为不存在“部分依赖”);若候选码是“复合属性”(如“学号+课程号”),则需检查非主属性是否依赖于候选码的某一个部分。

反例(满足1NF,但不满足2NF):学生选课表2(同1NF正例)

学号 姓名 课程号 课程名 成绩
2023001 张三 001 数学 90
2023001 张三 002 英语 85
2023002 李四 001 数学 88
2023002 李四 003 物理 92

第一步:确定候选码和非主属性

  • 候选码:(学号, 课程号)(只有同时知道“学号”和“课程号”,才能唯一确定一条选课记录的“成绩”);
  • 主属性:学号、课程号;
  • 非主属性:姓名、课程名、成绩。

第二步:分析函数依赖

  • 正常依赖(依赖于候选码全部):(学号, 课程号) → 成绩(必须同时知道学号和课程号,才能确定成绩);
  • 部分依赖(问题所在)
    • 学号 → 姓名(仅需候选码的“学号”部分,就能确定姓名,与课程号无关);
    • 课程号 → 课程名(仅需候选码的“课程号”部分,就能确定课程名,与学号无关)。

导致的问题(数据冗余+操作异常)

  1. 数据冗余:张三的姓名重复存储(选几门课就存几次),数学的课程名也重复存储;
  2. 更新异常:若张三改姓名,需修改所有包含“2023001”的记录,漏改则数据不一致;
  3. 插入异常:若新增一门课程(如004,语文),但暂无学生选课,因缺少“学号”(候选码的一部分),无法插入课程信息;
  4. 删除异常:若删除李四的所有选课记录(如001和003),则“李四”的姓名信息也会被同时删除,无法单独保留学生基本信息。

正例(满足2NF):拆分表为3个表

表1:学生表(候选码:学号,单一属性,天然满足2NF)

学号 姓名
2023001 张三
2023002 李四

表2:课程表(候选码:课程号,单一属性,天然满足2NF)

课程号 课程名
001 数学
002 英语
003 物理

表3:选课表(候选码:(学号, 课程号),非主属性仅“成绩”)

学号 课程号 成绩
2023001 001 90
2023001 002 85
2023002 001 88
2023002 003 92

改进:通过“拆分表”消除部分依赖——将“姓名”放入“学生表”(依赖于学号),“课程名”放入“课程表”(依赖于课程号),“选课表”仅保留候选码(学号, 课程号)和非主属性“成绩”(依赖于候选码全部),满足2NF。

3. 第三范式(3NF):消除非主属性对候选码的“传递函数依赖”

核心要求

  1. 首先满足2NF;
  2. 消除非主属性对候选码的传递依赖(即非主属性不能依赖于“另一个非主属性”,再间接依赖于候选码)。

关键判断:若非主属性A依赖于非主属性B,且B依赖于候选码,则A传递依赖于候选码,违反3NF。

反例(满足2NF,但不满足3NF):扩展学生表(新增“学院”和“学院院长”)

表1:学生表(候选码:学号,满足2NF,但不满足3NF)

学号 姓名 学院 学院院长
2023001 张三 计算机学院 王教授
2023002 李四 数学学院 李教授
2023003 王五 计算机学院 王教授

第一步:确定候选码和非主属性

  • 候选码:学号(单一属性,满足2NF);
  • 主属性:学号;
  • 非主属性:姓名、学院、学院院长。

第二步:分析函数依赖

  • 正常依赖:学号 → 姓名学号 → 学院
  • 传递依赖(问题所在)学号 → 学院 → 学院院长(“学院院长”不直接依赖于候选码“学号”,而是依赖于非主属性“学院”,再间接依赖于“学号”,属于传递依赖)。

导致的问题

  1. 数据冗余:“计算机学院”的院长“王教授”重复存储(所有该学院的学生记录都存一次);
  2. 更新异常:若计算机学院换院长(如换成张教授),需修改所有“计算机学院”学生的记录,漏改则数据不一致;
  3. 插入异常:若新增一个“化学学院”,但暂无学生,因缺少“学号”(候选码),无法插入“化学学院”和其院长信息;
  4. 删除异常:若删除所有“数学学院”的学生记录,“数学学院”和“李教授”的信息也会被删除,无法单独保留学院信息。

正例(满足3NF):再次拆分表为2个表

表1:学生表(仅保留直接依赖于学号的属性)

学号 姓名 学院
2023001 张三 计算机学院
2023002 李四 数学学院
2023003 王五 计算机学院

表2:学院表(候选码:学院,非主属性“学院院长”直接依赖于学院)

学院 学院院长
计算机学院 王教授
数学学院 李教授
化学学院 赵教授

改进:通过拆分表消除传递依赖——将“学院院长”从“学生表”中剥离,放入“学院表”,“学院院长”直接依赖于“学院”(候选码),“学生表”中的“学院”直接依赖于“学号”(候选码),无传递依赖,满足3NF。

三者核心区别总结表

范式 核心约束(基于1NF) 解决的问题 依赖类型限制 举例中的关键改进
1NF 属性原子化,不可再分 数据不可拆分导致的查询低效 无(仅要求原子性) 将“选课信息”拆分为“课程号+课程名+成绩”
2NF 消除非主属性对候选码的部分依赖 复合候选码下的冗余与异常 非主属性必须依赖候选码全部 拆分“学生选课表”为“学生表+课程表+选课表”
3NF 消除非主属性对候选码的传递依赖 非主属性间接依赖导致的冗余 非主属性不能依赖非主属性 拆分“学生表”为“学生表+学院表”

关键结论

  • 范式等级越高,数据冗余越少,操作异常越少,但表的数量可能越多(需通过外键关联查询);
  • 实际设计中,3NF是最常用的标准(满足3NF的表已能解决绝大多数冗余和异常问题),更高范式(如BCNF)仅在特殊场景(多候选码交叉依赖)中使用;
  • 所有范式的本质是“通过拆分表,让每个属性只依赖于它的直接主属性(候选码)”。
posted @ 2025-09-28 16:25  姚春辉  阅读(138)  评论(0)    收藏  举报