数据库范式

发布于:2025-08-10 ⋅ 阅读:(17) ⋅ 点赞:(0)

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

第一范式(1NF)是关系数据库设计中最基本的规范化形式,其核心要求是原子性(Atomicity)。

原子性的含义

原子性要求数据库表中的每个列(字段)都包含不可再分的最小数据单元,即:

  1. 每个字段的值都是单一的、不可分割的
  2. 不允许在一个字段中存储多个值或复合值
  3. 不允许存在重复的列或列组

违反1NF的示例

以下表结构违反了1NF:

学号

姓名

联系电话

1001

张三

13800138000,13800138001

1002

李四

13900139000

问题在于"联系电话"列包含了多个值(用逗号分隔)。


符合1NF的修正方案

方案一:拆分多值字段

学生表(1NF):

学号

姓名

联系电话

1001

张三

13800138000

1001

张三

13800138001

1002

李四

13900139000

方案二:创建关联表

学生基本信息表:

学号

姓名

1001

张三

1002

李四

学生电话表:

学号

联系电话

1001

13800138000

1001

13800138001

1002

13900139000

1NF的其他要求

除了原子性外,1NF还要求:

  • 表中的每一行必须是唯一的(通常通过主键保证)
  • 列的顺序不重要
  • 行的顺序不重要

满足1NF是数据库规范化的第一步,为后续的2NF、3NF等更高级别的规范化奠定了基础。


1NF的实际意义

  1. 禁止"大杂烩字段"
    —— 比如不能把"电话1,电话2,电话3"挤在一个格子里,必须拆成多行或多列
  2. 消灭表格里的"套娃数据"
    —— 像"地址:北京市{海淀区{中关村大街5号}}"这种嵌套结构要拍平
  3. 让数据库能正常做计算
    —— 如果一列里既有数字又有文字,连SUM求和都没法用
  4. 避免数据黏成一团
    —— 比如"张三,李四,王五"挤在一个单元格里,想单独查李四的记录会疯掉

(本质上就是逼着你把数据拆到最小颗粒度,就像乐高积木必须用单个颗粒而不是粘死的整块)


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

第二范式(2NF)建立在第一范式(1NF)的基础上,主要解决部分函数依赖的问题。

2NF的核心要求

  1. 首先满足1NF的所有条件
  2. 所有非主键字段必须完全函数依赖于整个主键(不能只依赖于主键的一部分)

"每个非主键信息必须由整个主键决定,不能只由主键的一部分决定。"

比如学生选课表中,成绩由"学号+课程号"共同决定(符合2NF),但学生姓名只由学号决定(违反2NF)。

关键概念解释

  • 函数依赖:如果知道X的值就能确定Y的值,则称Y函数依赖于X(X→Y)
  • 完全函数依赖:非主键字段依赖于整个主键,而不是主键的某一部分
  • 部分函数依赖:非主键字段只依赖于主键的一部分(违反2NF)

违反2NF的经典例子

学生选课表(违反2NF)

学号

课程号

课程名称

学分

成绩

学生姓名

学生系别

001

C01

数据库

4

90

张三

计算机

001

C02

算法

3

85

张三

计算机

002

C01

数据库

4

88

李四

数学

问题分析:

  1. 主键是复合主键(学号+课程号)
  2. "成绩"字段完全依赖于整个主键(学号+课程号)→ 符合2NF
  3. 但"课程名称"和"学分"只依赖于"课程号"(主键的一部分)→ 部分依赖
  4. "学生姓名"和"学生系别"只依赖于"学号"(主键的一部分)→ 部分依赖

符合2NF的解决方案

将表拆分为三个表,消除部分依赖:

1. 学生表(主键:学号)

学号

学生姓名

学生系别

001

张三

计算机

002

李四

数学

2. 课程表(主键:课程号)

课程号

课程名称

学分

C01

数据库

4

C02

算法

3

3. 选课成绩表(主键:学号+课程号)

学号

课程号

成绩

001

C01

90

001

C02

85

002

C01

88

为什么这样设计符合2NF?

  1. 学生表中,所有非主键字段(姓名、系别)完全依赖于主键(学号)
  2. 课程表中,所有非主键字段(名称、学分)完全依赖于主键(课程号)
  3. 选课成绩表中,成绩字段完全依赖于整个主键(学号+课程号)

这样就消除了所有部分函数依赖,达到了2NF的要求。

2NF的实际意义

  • 减少数据冗余(如课程信息不再重复存储)
  • 避免更新异常(如修改某课程名称只需修改一处)
  • 为后续的3NF规范化奠定基础

2NF的实际意义

"消灭重复数据,避免改一处要动全身的麻烦"

具体来说:

  1. 拒绝"捆绑销售"
    —— 比如学生选课表里,每次选课都重复存储学生姓名、系别(浪费空间)
  2. 避免"牵一发动全身"
    —— 如果张三转系,不用在几百条选课记录里逐个修改他的系别,只需改学生表里的一条数据
  3. 让数据各回各家
    —— 学生信息归学生表,课程信息归课程表,成绩单独存(就像图书馆把书、借阅记录、读者信息分开管理)

本质:把"一坨数据"拆成多个专业小表,像乐高说明书要求你把零件先分类,而不是混在一起乱拼。


第三范式(3NF):消灭“间接依赖”

一句话解释:“所有非主键字段必须直接依赖主键,不能通过其他非主键字段间接依赖。”

(简单说:表中的数据只能由主键决定,不能由其他非主键字段决定。)


3NF的核心要求

  1. 首先满足2NF(所有非主键字段完全依赖主键)。
  2. 不能存在“传递依赖”(即A→B→C,但C不直接依赖A)。

违反3NF的例子

学生住宿表(违反3NF)

学号

姓名

宿舍楼

宿舍费用

1001

张三

A栋

1200

1002

李四

B栋

1500

1003

王五

A栋

1200

问题分析:

  • 主键是学号(唯一标识学生)。
  • 宿舍费用并不直接由学号决定,而是由宿舍楼决定(学号→宿舍楼→宿舍费用)。
  • 这导致:
    • 数据冗余(A栋的费用重复存储)。
    • 更新麻烦(如果A栋费用涨到1300,要改多条记录)。

符合3NF的解决方案

拆成两个表,消除传递依赖:

1. 学生表(主键:学号)

学号

姓名

宿舍楼

1001

张三

A栋

1002

李四

B栋

1003

王五

A栋

2. 宿舍费用表(主键:宿舍楼)

宿舍楼

宿舍费用

A栋

1200

B栋

1500

为什么这样更好?

  • 减少冗余:宿舍费用只存一次。
  • 更新方便:改A栋费用只需改一条记录。
  • 查询灵活:可以单独查宿舍费用,不影响学生信息。

3NF的实际意义

  1. 避免“数据连环改”(比如宿舍费用调整时,不用逐个修改学生记录)。
  2. 减少存储浪费(相同数据不重复存)。
  3. 让表更“纯粹”(每张表只描述一种东西,比如学生表只管学生,宿舍表只管宿舍)。

总结:

  • 1NF:数据拆到最小(不能一个格子存多个值)。
  • 2NF:数据不能“半依赖”主键(必须完全依赖)。
  • 3NF:数据不能“拐弯依赖”主键(必须直接依赖)。

3NF之后还有BCNF、4NF等,但3NF已经能解决大多数实际问题!


问三个问题:

  1. 所有字段都是原子的吗?(1NF)
  2. 所有非主键字段都完全依赖整个主键吗?(2NF)
  3. 非主键字段之间没有依赖关系吗?(3NF)


网站公告

今日签到

点亮在社区的每一天
去签到