2314 字
12 分钟
数据库---范式
2026-03-16

前言#

范式(Normal Form)是数据库设计中的一套理论,用于消除数据冗余、减少数据异常。它通过一系列规则,将复杂的表结构分解为更简单、更规范的形式。

简单来说,范式就是**“拆表”的规矩**,级别越高,规矩越严,表拆得越细。

范式#

建议:在大多数业务系统中,设计到 3NF 通常就足够了,既能保证数据规范,又不会因为过度拆分导致查询性能下降。

第一范式 (1NF):原子性#

核心要求:表中的每个字段都必须是不可再分的最小数据单元(原子性)。

  • 通俗理解:一个格子只能放一个值,不能放“一篮子”东西。
  • 违反示例:一个“爱好”字段里存了“篮球,足球,音乐”三个值。
  • 修正方法:要么拆成三个字段(爱好1、爱好2、爱好3),要么拆成多行(一个人对应多条爱好记录)。

第二范式 (2NF):消除部分依赖#

前提:必须满足 1NF。

核心要求:表中所有非主键字段必须完全依赖于主键,不能只依赖主键的一部分。

  • 通俗理解:一张表只描述一件事。如果主键由多个字段组成,非主键字段不能只跟其中一部分有关系。
  • 违反示例:选课表(学号, 课程号, 成绩, 学生姓名)。
    • 问题:学生姓名只依赖于“学号”,不依赖于“课程号”,这就是部分依赖。
  • 修正方法:拆表。把“学生姓名”单独放到学生表里。

第三范式 (3NF):消除传递依赖#

前提:必须满足 2NF。

核心要求:非主键字段之间不能有依赖关系(即不能存在 A->B->C 的传递链)。

  • 通俗理解:非主键字段之间不能“互相决定”。
  • 违反示例:学生表(学号, 姓名, 学院, 院长)。
    • 问题:学号决定学院,学院决定院长,院长传递依赖于学号。
  • 修正方法:拆表。把“学院”和“院长”单独放到学院表里。

BCNF (Boyce-Codd 范式):更严格的 3NF#

核心要求:主键中的任何一个字段(或候选键)都不能被非主键字段决定。

  • 通俗理解:在 3NF 的基础上,进一步消除了主键字段之间的“内部依赖”。
  • 违反示例:仓库表(仓库ID, 存储物品, 管理员ID),假设一个仓库可以存多种物品,一个管理员只负责一个仓库。
    • 问题:管理员ID 决定了 仓库ID(即非主键决定了主键的一部分)。
  • 修正方法:拆表。把“管理员”和“仓库”的关系单独成表。

范式拆分示例#

初始表格#

学生选课记录(非1NF状态)

学号姓名班级课程列表教师教师职称课程号成绩
1001张三软件1班数据库(S001),数据结构(S002)王老师, 李老师教授, 副教授C001, C00285, 92
1002李四软件1班操作系统(S003)赵老师讲师C00388

这张表存在以下违反范式的问题:

  • 违反1NF:“课程列表”、“教师”、“教师职称”等字段包含多个值,不是原子数据。
  • 主键模糊:无法用一个字段唯一标识一条记录。

满足1NF(确保原子性)#

解决方式:将复合值的字段拆成多行,使每行、每列都对应一个单一值。

学号姓名班级课程号课程名教师教师职称成绩
1001张三软件1班S001数据库王老师教授85
1001张三软件1班S002数据结构李老师副教授92
1002李四软件1班S003操作系统赵老师讲师88

此时变化

  • 一个学生选多门课,就对应多行数据。
  • 可以设定联合主键为 (学号, 课程号),因为这两个字段可以唯一确定一行。
  • 但还存在数据冗余更新异常
    • 如果“张三”从“软件1班”转到“软件2班”,需要修改他所有选课记录。
    • “教师职称”完全依赖于“教师”,而不是主键,这为后续范式要解决的问题。

满足2NF(消除部分依赖)#

核心:在满足1NF的基础上,消除非主属性对主键的部分依赖。即,非主属性必须完全依赖于整个主键

解决方案:将表拆解,让每张表描述一个独立的事物。

拆表结果

  1. 学生表(描述学生实体)
学号(主键)姓名班级
1001张三软件1班
1002李四软件1班
  1. 课程表(描述课程实体)
课程号(主键)课程名教师
S001数据库王老师
S002数据结构李老师
S003操作系统赵老师
  1. 选课成绩表(描述学生和课程的关系)
学号(外键)课程号(外键)成绩
1001S00185
1001S00292
1002S00388

此时,每张表都满足2NF。选课成绩表中,唯一的非主属性成绩完全依赖于联合主键(学号, 课程号)

满足3NF(消除传递依赖)#

核心:在满足2NF的基础上,消除非主属性之间的传递依赖。即,任何非主属性不能依赖于其他非主属性。

分析我们现有的表

  • 学生表、选课成绩表已满足3NF。
  • 问题在课程表
    • 主键是课程号,非主属性是课程名教师
    • 看起来没问题?但还有一个字段教师职称我们之前忽略了。假设我们最初的表格在满足1NF时,把它加回来。那么课程表是这样的:
课程号(主键)课程名教师教师职称
S001数据库王老师教授
S002数据结构李老师副教授
S003操作系统赵老师讲师

传递依赖

  • 依赖链:课程号-> 教师-> 教师职称

也就是说,教师职称不是直接由主键课程号决定,而是由非主键教师决定的。(传递依赖!)

解决方案:将与主键无关的属性拆分出去,修改原来的课程表,新增一个教师表。

  1. 新课程表
课程号(主键)课程名教师编号(外键)
S001数据库T01
S002数据结构T02
S003操作系统T03
  1. 教师表
教师编号(主键)教师教师职称
T01王老师教授
T02李老师副教授
T03赵老师讲师

现在所有表都满足3NF。

满足 BCNF (消除主属性对非主属性的依赖)#

核心:在满足3NF的基础上,所有决定因素都必须是候选键。更直观地说,要消除“主属性被非主属性决定”或“候选键的一部分被非主属性决定”的情况。

我们的3NF表看似完美。为了展示BCNF的违反情况,我们修改一个业务场景

假设这样一个案例:一位教师只教一门课,但一门课可以有多个学生,一个学生可以选择多门课

学生课程教师
张三数据库王老师
李四数据库王老师
张三数据结构李老师

分析

  • 候选键:(学生, 课程)(学生, 教师)都可以作为候选键。我们选(学生, 课程)作主键。

  • 存在函数依赖:教师-> 课程(因为一位教师只教一门课)。

违反BCNF

决定因素教师-> 课程,但教师不是这个表的超键(仅凭教师无法唯一确定一行,因为一个老师教多个学生)。

问题:如果王老师不教数据库了,需要修改多行数据(更新异常)。

满足BCNF的解决方案

拆分成两张表,让每个函数依赖的决定因素都是其所在表的超键。

  1. 教课表(教师是决定因素,作主键)
教师(主键)课程
王老师数据库
李老师数据结构
  1. 选课表
学生教师(外键)
张三王老师
李四王老师
张三李老师

总结#

范式解决问题拆分动作
1NF属性原子性将包含多个值的单元格拆分为多行
2NF消除部分函数依赖(学号,课程号)为主键的表,拆分为学生实体表课程实体表联系表
3NF消除传递函数依赖从课程表中,将依赖于教师的教师职称属性拆分到独立的教师表
BCNF决定因素必须是候选键在更复杂的“教师-课程-学生”关系中,拆分以确保每个表的决定因素都是超键。

最终,通过逐步范式化,我们得到了一个结构清晰、冗余极低、基本无更新异常的数据库模型,通常包含:

  • 学生表
  • 课程表
  • 教师表
  • 选课关系表教学关系表

小测验#

关系模式R中属性全是主属性,则R的最高范式必定是哪种?

答: 关系模式 R 中属性全是主属性,则 R 的最高范式必定是 3NF,但不一定是 BCNF。

数据库---范式
https://lettle.cn/posts/normalform/
作者
Lettle
发布于
2026-03-16
许可协议
CC BY-NC-SA 4.0