数据库的规范化设计方法---3种范式

发布于:2025-05-18 ⋅ 阅读:(12) ⋅ 点赞:(0)

第一范式(1NF):确保表中的每个字段都是不可分割的基本数据项。
第二范式(2NF):在满足1NF的基础上,确保非主属性完全依赖于主键。
第三范式(3NF):在满足2NF的基础上,确保非主属性不传递依赖于主键。

使用 StudentCourse 表作为基础,来解释第一范式(1NF)。

第一范式 (1NF) 的核心要求:
确保表中的每一个单元格(字段值)都包含单一、不可再分割的基本数据项。换句话说,表中的每一列都必须是原子性的,不能包含多个值或复合值。
回顾之前的 StudentCourse 表:

StudentID (学号) StudentName (姓名) StudentDepartment (系别) CourseID (课程号) CourseName (课程名) Professor (教授)
1001 张三 计算机系 CS101 数据结构 李教授
1001 张三 计算机系 CS201 数据库原理 王教授
1002 李四 电子系 EE101 电路基础 赵教授
1003 王五 计算机系 CS101 数据结构 李教授
1003 王五 计算机系 CS301 操作系统 刘教授

分析 StudentCourse 表是否满足 1NF:
现在我们来逐一检查这个表中的每一列,看看它们是否都满足原子性(不可分割)的要求:

  1. StudentID (学号): 例如 “1001”。这是一个单一的数值,不能再分割成更小的有意义的部分(比如不能把它分成 ‘1’, ‘0’, ‘0’, ‘1’ 并认为每个都有独立意义)。满足 1NF。
  2. StudentName (姓名): 例如 “张三”。虽然姓名可以拆分成姓氏和名字(“张” 和 “三”),但在数据库设计中,通常将整个姓名视为一个不可分割的字符串来处理,特别是当我们不需要单独查询姓氏或名字时。如果业务上需要单独处理姓氏和名字,才需要拆分成两列。通常认为满足 1NF。
  3. StudentDepartment (系别): 例如 “计算机系”。这是一个单一的文本值,表示一个系别。满足 1NF。
  4. CourseID (课程号): 例如 “CS101”。这是一个单一的标识符,不能再分割。满足 1NF。
  5. CourseName (课程名): 例如 “数据结构”。这是一个单一的文本值。满足 1NF。
  6. Professor (教授): 例如 “李教授”。这是一个单一的文本值。满足 1NF。
    结论: 在这个例子中,StudentCourse 表的每一列都只包含单一、不可分割的数据项。因此,这个表满足第一范式 (1NF)
    一个不满足 1NF 的例子:
    假设我们有一个 StudentInfo 表,其中存储了学生的联系电话,但允许一个学生有多个电话:
StudentID StudentName PhoneNumbers
1001 张三 13812345678, 13987654321
1002 李四 13700001111
1003 王五 13611112222, 13833334444, 13955556666

在这个表中,PhoneNumbers 列包含了多个电话号码,用逗号分隔。这违反了 1NF,因为该列的单元格包含了多个基本数据项(多个电话号码)。
要使其满足 1NF,我们需要将每个电话号码放在单独的一行,或者创建一个单独的 StudentPhone 表。
希望这个例子能帮助你理解第一范式的要求!

好的,我们来通过具体的例子来解释“完全依赖”和“不传递依赖”这两个概念,它们是第二范式(2NF)和第三范式(3NF)的核心。
假设我们有一个存储学生选修课程信息的表:
表:StudentCourse (学生选课表)

StudentID (学号) StudentName (姓名) StudentDepartment (系别) CourseID (课程号) CourseName (课程名) Professor (教授)
1001 张三 计算机系 CS101 数据结构 李教授
1001 张三 计算机系 CS201 数据库原理 王教授
1002 李四 电子系 EE101 电路基础 赵教授
1003 王五 计算机系 CS101 数据结构 李教授
1003 王五 计算机系 CS301 操作系统 刘教授

这个表的主键是 (StudentID, CourseID),因为一个学生可以选多门课,一门课可以被多个学生选,只有同时知道学号和课程号才能唯一确定一行记录。

2. 第二范式 (2NF) - 完全依赖 (Full Dependency)

定义: 在满足第一范式(1NF)的基础上,非主属性必须完全依赖于整个主键,而不仅仅是主键的一部分。
问题: 如果主键是由多个属性组成的(复合主键),我们需要检查每个非主属性是否依赖于所有主键属性。
在 StudentCourse 表中:

  • 主键: (StudentID, CourseID)
  • 非主属性: StudentName, StudentDepartment, CourseName, Professor
    现在我们检查每个非主属性是否完全依赖于 (StudentID, CourseID)
  1. StudentName (姓名): 姓名是由学号 StudentID 决定的,和选了哪门课 CourseID 无关。所以 StudentName 依赖于 StudentID,而不是 (StudentID, CourseID)。这叫部分依赖 (Partial Dependency)
  2. StudentDepartment (系别): 系别也是由学号 StudentID 决定的,和课程号 CourseID 无关。所以 StudentDepartment 依赖于 StudentID,而不是 (StudentID, CourseID)。这也是部分依赖
  3. CourseName (课程名): 课程名是由课程号 CourseID 决定的,和学生的学号 StudentID 无关。所以 CourseName 依赖于 CourseID,而不是 (StudentID, CourseID)。这同样是部分依赖
  4. Professor (教授): 教授通常是由课程号 CourseID 决定的(假设一门课只有一个固定教授),和学生的学号 StudentID 无关。所以 Professor 依赖于 CourseID,而不是 (StudentID, CourseID)。这也是部分依赖
    结论: StudentCourse 表不满足 2NF,因为存在非主属性(StudentName, StudentDepartment, CourseName, Professor)部分依赖于主键 (StudentID, CourseID) 的子集(要么只依赖 StudentID,要么只依赖 CourseID)。
    如何修正 (分解): 我们需要将表分解,使得每个表的非主属性都完全依赖于该表的主键。
  • Student (学生表): 主键 StudentID
    StudentID (学号) StudentName (姓名) StudentDepartment (系别)
    1001 张三 计算机系
    1002 李四 电子系
    1003 王五 计算机系
  • Course (课程表): 主键 CourseID
    CourseID (课程号) CourseName (课程名) Professor (教授)
    CS101 数据结构 李教授
    CS201 数据库原理 王教授
    EE101 电路基础 赵教授
    CS301 操作系统 刘教授
  • StudentSC (学生选课关系表): 主键 (StudentID, CourseID)
    StudentID (学号) CourseID (课程号)
    1001 CS101
    1001 CS201
    1002 EE101
    1003 CS101
    1003 CS301

现在检查这些新表:

  • Student 表的主键是 StudentID,所有非主属性 (StudentName, StudentDepartment) 都完全依赖于 StudentID
  • Course 表的主键是 CourseID,所有非主属性 (CourseName, Professor) 都完全依赖于 CourseID
  • StudentSC 表的主键是 (StudentID, CourseID),它没有其他非主属性,所以自然满足。
    这些分解后的表都满足了 2NF。

3. 第三范式 (3NF) - 不传递依赖 (No Transitive Dependency)

定义: 在满足第二范式(2NF)的基础上,非主属性必须不传递依赖于主键。也就是说,非主属性不能依赖于另一个非主属性。
问题: 我们需要检查是否存在“主键 -> 非主属性 A -> 非主属性 B”这样的依赖链条,其中 B 就是传递依赖于主键。
在修正后的 Student 表中:

  • 主键: StudentID
  • 非主属性: StudentName, StudentDepartment
    检查是否存在传递依赖:
  • StudentID -> StudentDepartment (学号决定系别)
  • StudentDepartment -> ? 这个系别 (StudentDepartment) 是否又决定了其他的非主属性?在我们的 Student 表中,系别只决定了它自己,没有决定其他非主属性 (StudentName)。所以没有传递依赖。
    在修正后的 Course 表中:
  • 主键: CourseID
  • 非主属性: CourseName, Professor
    检查是否存在传递依赖:
  • CourseID -> Professor (课程号决定教授)
  • Professor -> ? 这个教授 (Professor) 是否又决定了其他的非主属性?在我们的 Course 表中,教授只决定了它自己,没有决定其他非主属性 (CourseName)。所以没有传递依赖。
    假设我们有一个不满足 3NF 的表:
    表:StudentDept (学生系别表 - 假设大学有系主任)
StudentID (学号) StudentName (姓名) StudentDepartment (系别) DepartmentHead (系主任)
1001 张三 计算机系 陈主任
1002 李四 电子系 吴主任
1003 王五 计算机系 陈主任
  • 主键: StudentID (假设学号唯一)
  • 非主属性: StudentName, StudentDepartment, DepartmentHead
    检查依赖关系:
  1. StudentID -> StudentName (满足)
  2. StudentID -> StudentDepartment (满足)
  3. StudentID -> DepartmentHead ? 不完全是。学号本身不直接决定系主任。但是,我们可以通过学号找到系别 (StudentID -> StudentDepartment),然后通过系别找到系主任 (StudentDepartment -> DepartmentHead)。所以,DepartmentHead传递依赖于主键 StudentID 的。StudentID -> StudentDepartment -> DepartmentHead
    结论: StudentDept 表不满足 3NF,因为存在非主属性 DepartmentHead 传递依赖于主键 StudentID
    如何修正 (分解): 我们需要将传递依赖的非主属性分离出来。
  • Student (学生表): 主键 StudentID
  • StudentID (学号) StudentName (姓名) StudentDepartment (系别)
    1001 张三 计算机系
    1002 李四 电子系
    1003 王五 计算机系
  • Department (系别表): 主键 StudentDepartment (可以用系别名称或系编号作为主键,这里简化用名称)
    StudentDepartment (系别) DepartmentHead (系主任)
    计算机系 陈主任
    电子系 吴主任

现在检查这些新表:

  • Student 表满足 3NF。
  • Department 表的主键是 StudentDepartment,唯一的非主属性 DepartmentHead 完全依赖于它,并且没有其他非主属性可以依赖,所以也满足 3NF。

总结:

  • 完全依赖 (2NF): 非主属性不能只依赖于复合主键的“一部分”。它们必须依赖于整个主键。
  • 不传递依赖 (3NF): 非主属性不能依赖于另一个非主属性。它们必须直接依赖于主键,不能通过中间的非主属性间接依赖。
    通过满足这些范式,可以减少数据冗余,避免数据更新异常(插入、删除、修改异常)。

网站公告

今日签到

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