跳至主要内容

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

了解第三范式(3NF)如何通过移除冗余、降低依赖问题,帮助您更高效地组织数据库。看看如何通过表分解来简化数据管理。
· 9分钟 阅读

使用 AI 探索

想象一下,您在处理一个庞大且无结构的数据库,里面充斥着重复、冗余的信息。每一次更新或删除操作都可能酿成灾难,带来错误与不一致。第三范式(3NF)是一种行之有效的数据库规范化方法,可以避免这种混乱。实施 3NF 能清理您的数据结构,确保其高效、有序,并且没有不必要的冗余。

本文将探讨 3NF 的工作原理、价值所在以及如何落地实施。我们还会将 3NF 与其他范式进行对比,了解各自的适用场景。任何人学习这些结构都会受益,但如果您是数据库设计师或数据科学家,这方面的知识尤其重要,因为它能显著简化您的工作并让数据库更可靠。如果您对整体的数据库设计感兴趣,不妨看看我们的完整数据库设计课程。

第三范式(3NF)定义

第三范式是数据库规范化中的关键概念,旨在移除不必要的依赖。3NF 建立在第一范式(1NF)和第二范式(2NF)之上,也就意味着它继承了它们的规则:1NF 要求每个单元格的值是原子的(不可再分),2NF 要求消除对复合主键的部分依赖。在此基础上,3NF 进一步消除传递依赖,即非键属性通过其他非键属性间接依赖主键的情况。

通过聚焦这一点,3NF 确保表中的每个非键列都只直接依赖于主键,而不依赖其他任何因素。从更实际的角度看,3NF 有助于在插入、更新或删除数据时尽量减少冗余并避免异常。

20 世纪 70 年代,Edgar F. Codd 提出了 3NF,用以形式化实现完全规范化的数据库结构所需的条件。几年后,Carlo Zaniolo 对其进行了重新表述,更清晰地阐明了“经典”3NF 与更严格的 Boyce-Codd 范式(BCNF)之间的差异。先别着急关注 BCNF,我们将在后文再谈。

理解第三范式的条件

那么,实现 3NF 究竟需要满足哪些条件?一张表要符合 3NF,需要满足以下几点:

  • 满足 2NF:这意味着数据已是原子的,没有重复分组,也不存在对任何复合键的部分依赖。

1NF, 2NF, and 3NF requirements

3NF 包含 2NF 和 1NF。图:作者自制

  • 无传递依赖:这是关键规则。在 3NF 的表中,任何非主键列都必须只依赖于主键,而不能通过其他非键列间接依赖。

下面我们来看这在实践中意味着什么。

分解表以实现 3NF

我们一起来走一遍将表分解至 3NF 的过程。文中将使用一些来自 DataCamp 课程的示例数据展示每一步。

步骤 1:识别传递依赖

首先,我们要寻找表中是否存在对主键的间接依赖。经验法则是:如果某个属性依赖的不是主键本身,那就意味着存在传递依赖。这表明可能需要对表进行拆分。

看看下面三张表。哪一张存在传递依赖?

表 1:Course(课程)

Course IDCourse NameDifficulty
201SQL FundamentalsBeginner
202Introduction to PythonBeginner
203Understanding Data ScienceIntermediate

表 2:Instructor(讲师)

Instructor IDInstructor NameExpertise
1Sarah JohnsonData Science
2Tom WilliamsMachine Learning
3Emily BrownPython

表 3:Enrollments(选课)

Enrollment IDStudent NameCourse IDCourse Name
1001Alice Smith201SQL Fundamentals
1002Bob Green202Introduction to Python
1003Charlie Blue201SQL Fundamentals

答案是……表 3!

在这张表中,Course Name 依赖于 Course ID,但并不直接依赖于Enrollment ID(主键)。这种间接依赖使得Course Name 成为传递依赖。

步骤 2:将数据拆分到新表

为了解决传递依赖,我们将把表 1 拆成两张表。每张表只关注直接依赖的数据。

修订后的选课表

Enrollment IDStudent NameCourse ID
1001Alice Smith201
1002Bob Green202
1003Charlie Blue201

课程表

Course IDCourse Name
201SQL Fundamentals
202Introduction to Python

现在,每张表只包含直接依赖其主键的信息:Course ID 现在是Course Name 在Courses 表中的主键,而Enrollment ID 是Enrollments 表中的主键。

通过这种分解,这些表现已满足 3NF 要求,消除了冗余,确保每张表只存储直接相关的信息。

如果您想亲手创建自己的数据库,可以看看我们的创建 PostgreSQL 数据库课程。如果您已有一定基础,可以尝试Snowflake 数据建模入门,其中涵盖实体-关系和维度建模等理念。

使用第三范式的优缺点

那么,为什么要费这么大劲去达到 3NF?主要优势如下:

  • 提升数据完整性:通过消除传递依赖,3NF 有助于确保更新和删除不会导致跨表数据冲突或过时。
  • 减少冗余:冗余更少意味着数据库更易维护,存储占用也更低。
  • 简化数据维护:将相似信息放入专门的表中,可以更轻松地更新记录,而不用追踪重复项。

话虽如此,虽然 3NF 支持数据准确性,但也可能导致数据更为分散,从而因额外的表连接让复杂查询变慢。在需要速度优先于规范化的场景中,BCNF 或 4NF 可能更为实际。

对比:第一、第二、第三范式与 BC 范式

让我们看看不同范式之间的差异。

对比表:第一、第二与第三范式

下面这张对比表有助于您理解 1NF、2NF 和 3NF 的要求。

Feature1NF2NF3NF
Atomic data✅✅✅
No partial dependencies❌✅✅
No transitive dependencies❌❌✅

Third normal form vs. Boyce-Codd normal form (BCNF)

BCNF 是 3NF 的“更严格”形式,可进一步消除因候选键重叠而产生的异常。它在复杂场景中尤其有用,当 3NF 仍未完全消除依赖时适用。BCNF 适用于这样的情况:某个非主属性依赖于复合候选键的一部分。听起来有点复杂,我们用一个例子来拆解。

当前结构(符合 3NF)

在为实现 3NF 而进行分解后,我们得到了如下两张表:

选课表

Enrollment IDStudent NameCourse ID
1001Alice Smith201
1002Bob Green202
1003Charlie Blue201

课程表

Course IDCourse Name
201SQL Fundamentals
202Introduction to Python

在这个结构中,每张表都处于 3NF,不存在传递依赖,数据已适当规范化。

引入新需求

现在,让我们在课程表(Courses)中加入一个新属性:Classroom(上课教室)。这个新属性可能会引发需要 BCNF 的场景。

更新后的课程表(3NF)

Course IDCourse NameClassroom
201SQL FundamentalsRoom 101
202Introduction to PythonRoom 102
203Understanding Data ScienceRoom 101

在这里,Course ID 仍是主键,其他属性都直接依赖于它。但我们假设有一条新规则:每间教室同一时间只能容纳一个科目。同时假设Course Name “SQL Fundamentals” 可在不同的Course ID(如 201、204 等)下开设,只要上课时间不同。在这种情况下,每次“SQL Fundamentals”的开课仍都在 “Room 101”,而不取决于具体的Course ID。因此,Course Name 也能唯一决定Classroom。

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

  1. Course ID
  2. Course Name

在这两个候选键并存的情况下,出现了 3NF 未覆盖的问题:Classroom 依赖于Course Name,而不仅仅是Course ID。

应用 BCNF

为消除这一依赖问题,我们需要进一步将Courses 表分解为两张更符合 BCNF 的表:

  1. 新的Courses 表,仅包含Course ID 和Course Name。
  2. 新增CourseDetails 表,存储Course Name 与Classroom 的对应关系。

如下所示:

修订后的课程表(BCNF)

CourseDetails 表(BCNF)

Course NameClassroom
SQL FundamentalsRoom 101
Introduction to PythonRoom 102
Understanding Data ScienceRoom 101

采用这一新结构后,每张表都满足 BCNF 的条件:

  • 在Courses 表中,Course ID 是主键,所有属性仅依赖于它。
  • 在CourseDetails 表中,Course Name 是主键,而Classroom 只依赖于Course Name。

这种设置消除了由候选键重叠引起的任何依赖问题,确保结构严格规范化。

结论

第三范式是数据库设计人员保持数据干净、一致、远离问题依赖的有力工具。借助 3NF,数据完整性得到增强,管理更顺畅,冗余更少。请记住,3NF 在大多数情况下都非常有效,但对于更复杂的数据库,诸如 BCNF 或 4NF 等更高范式可能会更合适。

如果这篇文章对您有所帮助,欢迎更进一步,获取我们的SQL 助理认证。这是验证您的 SQL 与数据库管理技能、并向潜在雇主展示您专业能力的绝佳方式!

Marie Fayard

作者

Marie Fayard

我是一名具有产品思维的技术负责人,专注于帮助早期初创公司从首个原型成长到实现产品与市场匹配,并持续向前发展。我对人们如何使用技术充满好奇,热爱与创始人及跨职能团队紧密合作,将大胆的想法变为现实。不在打造产品的时候,我会在世界的新角落寻找灵感,或在瑜伽馆放松减压。

关于第三范式的常见问题

3NF 能应用于所有类型的数据库吗?

虽然 3NF 在关系型数据库中十分有效,但对 NoSQL 数据库并非总是必要。NoSQL 通常更重视灵活性与可扩展性而非严格规范化。在某些场景下,尤其需要快速查询海量数据时,为了性能,可能更倾向于使用反规范化的模式。

严格遵循 3NF 有哪些缺点?

严格遵循 3NF 有时会产生包含众多数据表的复杂模式,查询时可能需要多次连接。这会对性能产生不利影响,尤其是在大型数据库或高事务量系统中。在这种情况下,反规范化或采用 BCNF 等替代方案可能更为实际。

3NF 能用于已有数据库吗?是否必须重新设计?

3NF 当然可以应用到现有数据库中,但可能需要较大幅度的重构。这个过程称为数据库重构,涉及将表分解以消除冗余和依赖。根据数据库规模与复杂度的不同,这一过程可能需要规划与测试,以确保数据完整性和系统性能得以保持。

有哪些工具或技术可以帮助自动实现 3NF?

有多种数据库设计工具可用,如 MySQL Workbench、Oracle SQL Developer 与 ER/Studio,可帮助可视化数据库模式并识别规范化问题。其中一些工具还能建议或自动执行实现 3NF 的步骤,但仍需要人工把控以确保数据完整性与一致性。

候选键与主键有什么区别?

候选键是能唯一标识表中每一行的最小属性集合。一张表可以有多个候选键。主键则是数据库设计者从候选键中选定的用于唯一标识行的那个键。每张表只能有一个主键,且其值不可为 NULL。

如果表已经处于第三范式(3NF),为什么还需要 Boyce-Codd 范式(BCNF)?

BCNF 比 3NF 更严格,专门处理依赖存在于候选键上的情形。尽管 3NF 消除了传递依赖,但若某个函数依赖的决定因素不是超键,仍可能出现冗余。BCNF 通过确保所有函数依赖的左侧都是超键来消除这一问题。

一张表是否可以有多个候选键?

是的,一张表可以有多个候选键。每个候选键都是能唯一标识行的唯一且最小的属性集合。

主题