什么是第三范式 (3NF)?

想象一下与一个庞大的、非结构化数据库打交道,里面充斥着重复、冗余的信息。每次更新或删除都可能引发潜在的灾难,存在错误和不一致性的风险。第三范式(3NF)是一个经过验证的数据库规范化方法,可避免这种混乱。实施3NF可以清理数据结构,确保其高效、有序,并且没有不必要的冗余。

在本文中,我们将探讨3NF的工作原理、其价值所在,以及如何将其付诸实践。我们还将比较3NF与其他形式,并学习何时使用每种形式。每个人都可以通过了解更多有关这些结构来受益,但如果您是数据库设计师或数据科学家,则这些知识尤为重要,因为它可以极大简化您的工作并确保数据库的可靠性。如果您对数据库设计整体感兴趣,请查看我们完整的数据库设计课程。

第三范式(3NF)定义

第三范式是数据库规范化的一个关键概念,它消除了不需要的依赖关系。 第三范式建立在第一范式(1NF)和第二范式(2NF)之上,意味着它继承了它们的规则:1NF要求每个单元格中都具有原子(不可分割)的值,2NF消除了对复合主键的部分依赖关系。 第三范式进一步消除了传递依赖,这是指非关键属性间接依赖于主键的情况。

通过专注于这一点,第三范式确保表中的每个非关键列都直接与主键相关联,而与其他内容无关。 更具体地说,第三范式有助于最小化冗余,并在插入、更新或删除数据时避免异常。

在1970年代,Edgar F. Codd引入了第三范式,以规范化数据库结构的达成条件。 几年后,Carlo Zaniolo的重新表述提供了对“经典”第三范式和更为严格的Boyce-Codd范式(BCNF)之间区别的更清晰解释。 现在不要太担心BCNF,我们稍后会回到这个话题。

理解第三范式的条件

那么,要实现第三范式需要什么条件?为了使表格符合要求,需要满足以下几个条件:

  • 已达到第二范式:这意味着它已经是原子的,没有重复组,并且不会对任何复合键存在部分依赖。

第三范式包括第二范式和第一范式。作者提供的图片

  • 没有传递依赖关系:这一规则至关重要。在第三范式表中,任何非主键列必须完全依赖于主键,不能间接依赖于另一个非键列。

让我们看看这实际上意味着什么。

分解表以实现第三范式

让我们逐步走过分解表以达到第三范式的过程。我们将使用来自DataCamp课程的一些示例数据来说明每个步骤。

步骤1:识别传递依赖关系

首先,我们将寻找表中任何间接依赖于主键的属性。作为一个经验法则,如果任何属性依赖于主键以外的内容,这表明存在传递依赖关系。这表明可能是时候拆分您的表了。

看一下下面的三个表。哪一个有传递依赖性?

表1:课程

Course ID Course Name Difficulty
201 SQL基础知识 初学者
202 Python入门 初学者
203 理解数据科学 中级

表2:讲师

Instructor ID Instructor Name Expertise
1 莎拉·约翰逊 数据科学
2 汤姆·威廉姆斯 机器学习
3 艾米莉·布朗 Python

表3:注册人数

Enrollment ID Student Name Course ID Course Name
1001 Alice Smith 201 SQL基础
1002 Bob Green 202 Python入门
1003 Charlie Blue 201 SQL基础

答案是… 表3!

在这个表中,课程名称取决于课程编号,但不直接取决于注册编号(主键)。这种间接依赖关系使得课程名称成为一个传递性依赖。

步骤2:将数据分离到新表中

为了解决传递依赖关系,我们将表1拆分成两个表。每个表将专注于直接相关的数据。

修订后的报名表

Enrollment ID Student Name Course ID
1001 Alice Smith 201
1002 Bob Green 202
1003 Charlie Blue 201

课程表

Course ID Course Name
201 SQL基础
202 Python入门

现在,每个表格只包含直接依赖于其主键的信息:课程ID现在是课程名称课程表中的主键,报名ID报名表中的主键。

通过这种分解,表现在满足3NF要求,消除了冗余数据,并确保每个表仅存储直接相关的信息。

如果您想动手创建自己的数据库,请查看我们的创建PostgreSQL数据库课程。如果您有一定经验,可以尝试Snowflake数据建模入门,其中涵盖了实体关系和维度建模等概念。

使用第三范式的优势和局限性

那么,为什么要花费这么多精力达到3NF呢?以下是主要优势:

  • 提高数据完整性:通过消除传递依赖关系,第三范式有助于确保更新和删除操作不会导致跨表的数据冲突或过时数据。
  • 减少冗余:减少冗余意味着您的数据库更易于维护,并且减少了存储使用。
  • 更简单的数据维护:将相似信息保存在专用表中使得更新记录变得更容易,无需追踪冗余条目。

话虽如此,虽然3NF结构支持数据准确性,但有时会导致数据更分散,有时由于额外的表连接使得复杂查询变慢。在需要速度优先于规范化的情况下,BCNF或4NF可能是更实用的选择。

比较:第一、第二、第三和BC规范形式

让我们来看看各种规范的区别。

比较表:第一、第二和第三范式

以下是一张比较表,帮助您了解1NF、2NF和3NF的要求。

Feature 1NF 2NF 3NF
原子数据
无部分依赖
无传递依赖

第三范式与Boyce-Codd规范形式(BCNF)

BCNF是3NF的一种“更严格”的形式,进一步消除了出现在重叠候选键中的异常情况。在复杂情况下,3NF单独并不能完全消除依赖关系时,BCNF尤其有用。当非主属性取决于组合候选键的一部分时,BCNF适用。我知道这听起来很复杂,所以让我们通过一个例子来解释。

当前结构(在3NF中)

分解以实现3NF后,我们得到了这两个表:

报名表

Enrollment ID Student Name Course ID
1001 Alice Smith 201
1002 Bob Green 202
1003 Charlie Blue 201

课程表

Course ID Course Name
201 SQL基础
202 Python简介

在这个结构中,每个表都是在第三范式,没有传递依赖,并且数据已经适当地规范化。

引入一个新需求

现在,让我们向Courses表中添加一个新属性:教室,即每门课程所在的教室。这个新属性可能导致需要BCNF。

更新后的课程表(第三范式)

Course ID Course Name Classroom
201 SQL基础 101教室
202 Python入门 102教室
203 数据科学入门 101教室

在这里,课程ID 仍然是主键,所有其他属性直接依赖于它。但让我们假设有一个新规则,即每个教室一次只能容纳一门课程。再假设课程名称SQL基础知识” 可以在不同的课程ID(如201、204等)下开设,只要它们安排在不同的时间。在这种情况下,每次开设”SQL基础知识“都会在”101教室”进行,而不管具体的课程ID。因此,课程名称 还可以唯一确定教室

这意味着我们现在有两个候选键:

  1. 课程编号
  2. 课程名称

有了两个候选键,我们现在遇到了一个三范式无法解决的问题:Classroom依赖于课程名称而不仅仅是课程ID

应用BCNF

为了消除这种依赖性问题,我们需要进一步将课程表分解为两个更符合BCNF的单独表:

  1. 一个新的课程表,只包括课程ID课程名称
  2. 一个CourseDetails表,用于存储课程名称教室的关联。

这是它的外观:

修订后的课程表(BCNF)

Course ID Course Name
201 SQL基础
202 Python入门
203 理解数据科学

课程详情表(BCNF)

Course Name Classroom
SQL基础 101教室
Python入门 102教室
理解数据科学 101教室

通过这种新的结构,每个表都满足BCNF条件:

  • 课程表中,课程ID是主键,所有属性都完全依赖于它。
  • CourseDetails表中,课程名称是主键,教室仅依赖于课程名称

此设置可消除由重叠候选键引起的依赖性问题,确保严格规范的结构。

结论

第三范式是数据库设计人员的宝贵工具,旨在保持数据清洁、一致且不受问题依赖性影响。通过第三范式,数据完整性得到增强,使管理更加顺畅,减少了冗余。请记住,虽然第三范式在大多数情况下运行良好,但更复杂的数据库可能会从其他形式(如BCNF或4NF)中受益。

如果您发现本文有用,请考虑迈出下一步,获得我们的SQL Associate Certification。这是验证您的SQL和数据库管理技能的绝佳方式,并向潜在雇主展示您的专业知识!

Source:
https://www.datacamp.com/tutorial/third-normal-form