数据规范化
规范化数据库的样式以及表结构为何如此重要。
数据规范化是为了减少数据库中的冗余并提高数据库效率而对信息进行结构化的过程。将规范化视为一种确保数据库中的每个字段和表都经过逻辑组织的组织方式,这样您就可以避免在插入、更新或删除记录时出现数据异常。此过程按照规定表应如何组织的特定规则进行。
规范化是更广泛的数据清理和标准化过程的一部分,该过程还包括确认数据的准确性、完整性,并且不包含重复记录,以及确保您为字段选择了合适数据类型。如果您从非规范化表开始,规范化过程将涉及创建额外的、更小的表,这些表可以通过外键连接在一起。也许您已经厌倦了在数据库中多个位置更新相同信息,只需一次值更改,或者发现您在删除记录时会丢失宝贵的数据。规范化表将有助于解决这两种情况。
本课程中将介绍的原则适用于关系型数据库管理系统(RDBMS)。如果您使用的是 NoSQL 或基于文档的数据库(如 MongoDB),以下信息将不适用。
简化和减少存储:规范化数据的优势
规范化旨在提高数据效率,以便您的团队能够找到并使用他们所需的信息。这些优势和规则在您熟悉数据库工作方式后可能看起来很合乎常理,但了解数据库中每个表和字段的明确目的非常重要。规范化数据的优势包括:
-
简化事务查询。使用规范化数据,查询客户地址只需要查看存储这些地址的单个字段。如果您在数据库中的不同位置多次存储客户地址,或者甚至在同一字段内保留多个地址,那么查询将需要更长时间才能执行。
-
减小数据库大小。如果您在数据库的几个位置重复客户数据,这意味着您为存储该信息腾出了空间。如果您的数据库只包含几个表,这可能不是一个主要问题,但如果您在大规模工作,磁盘空间可能非常宝贵。减少重复信息意味着削减存储成本,无论您是运行本地服务器还是依赖于云托管数据库。
-
简化数据库维护。想象一下,您的数据库中多次存储了相同的客户数据。每次客户更改地址时,都需要在每个“
客户地址”字段中更新,这很容易出错。如果您的数据已规范化,您将只有一个“客户地址”字段,该字段会连接到其他相关表,如订单。
数据异常
数据异常是指数据库中存储信息的不一致性。这些数据库结构上的缺陷在记录更新、添加或删除时出现问题时会变得明显。幸运的是,遵守规范化规则可以从一开始就防止这些异常发生。
更新异常
更新异常源于数据冗余。例如,假设您的数据库在多个表的字段中存储客户地址信息。客户更改地址可能只会导致其中一个字段更新为包含新信息,从而导致数据不一致。
插入异常
当一条记录在没有某些字段包含数据的情况下无法创建时,就会发生插入异常——这些数据可能还不存在。例如,一个非规范化的数据库可能被构造为,除非客户下订单,否则无法创建客户账户。规范化该数据库将通过创建单独的订单和客户表来解决此问题,并且没有禁止空值的规则。
删除异常
无意的信息丢失是删除异常的结果。假设您数据库中的一个表包含有关大学课程及其选修课程的学生的信息。如果一门课程因注册人数不足而取消,您可能会通过删除该课程记录而无意中丢失宝贵的学生信息。与插入异常一样,将数据分解为多个特定表可以防止此问题。
规范化规则
数据规范化的规则最早于 20 世纪 70 年代初引入。这些规则按称为范式的级别分组。每个级别都在前一个级别之上构建——您只能应用第二级规则,如果您的数据已经满足第一级规则,依此类推。虽然下面列出的三种范式之外还有更多范式,但前三种对于大多数用例来说已足够。
正如我们在数据库简介中介绍的那样,数据库中的表应包含一个实体键,也称为主键。此字段根据唯一 ID 区分表中的每一行,在连接表时很有用。在我们甚至能进入第一范式之前,您的表就需要有一个实体键字段。
第一范式 (1NF)
第一范式 (1NF) 要求表中的每个字段只能存储一个值,并且您的表不应包含多个存储相似信息的字段,例如标题为“地址1”和“地址2”的列。
这是一个我们将根据第一范式进行规范化的表示例。此表包含有关大学课程及其授课教师的信息。
教授表
| 教授 ID | 教授姓名 | 课程名称 |
|---|---|---|
| P001 | Gene Watson | 哲学入门;伦理学 |
| P002 | Melissa King | 量子力学 |
| P003 | Errol Tyson | 宏观经济学 |
| P004 | Mary Jacobson | 图形小说 |
我们注意到,虽然我们的字段是不同的,但一位教授(第一行中的 Gene Watson)教授两门课程,而这些信息目前存储在一个单元格中。如果我们根据 1NF 规范化此表,我们将需要将数据分解到多个表中。
规范化教授表
| 教授 ID | 教授姓名 |
|---|---|
| P001 | Gene Watson |
| P002 | Melissa King |
| P003 | Errol Tyson |
| P004 | Mary Jacobson |
规范化课程表
| 课程 ID | 课程名称 | 教授 ID |
|---|---|---|
| C001 | 哲学入门 | P001 |
| C002 | 伦理学 | P001 |
| C003 | 量子力学 | P002 |
| C004 | 宏观经济学 | P003 |
| C005 | 图形小说 | P004 |
由于一位教授可以教授多门课程,我们将这些数据分解为两个表。现在,我们的教授表与我们的课程表存在一对多关系。这个新的表结构满足第一范式,并通过外键(教授 ID字段)连接这两个表。
第二范式 (2NF)
第二范式旨在减少冗余并确保每个字段都描述了实体键所标识的内容。要满足 2NF,表中所有非实体键的字段都必须完全依赖于表的实体键(该实体键可能是由两个字段组成的复合键)。让我们看一个新示例——一个包含您的员工生日信息的表。
员工生日表
| 员工 ID | 生日 | 部门 |
|---|---|---|
| E001 | 11 月 18 日 | 会计 |
| E002 | 3 月 29 日 | 销售 |
| E003 | 6 月 1 日 | 市场营销 |
| E004 | 2 月 7 日 | 会计 |
此表满足 1NF,因为每列都不同,并且每个单元格中只包含一个值。但是,此表有一个复合键:“员工 ID + 生日”组合构成了表的实体键。此表目前不满足 2NF,因为部门字段仅部分依赖于复合键,因为员工的部门不依赖于他们的生日,只依赖于他们的员工 ID。为了解决此问题,我们将这些信息分解为两个表。
规范化员工生日表
| 员工 ID | 生日 |
|---|---|
| E001 | 11 月 18 日 |
| E002 | 3 月 29 日 |
| E003 | 6 月 1 日 |
| E004 | 2 月 7 日 |
规范化员工部门表
| 员工 ID | 部门 |
|---|---|
| E001 | 会计 |
| E002 | 销售 |
| E003 | 市场营销 |
| E004 | 会计 |
第三范式 (3NF)
如果一个表(除了满足 2NF 外)不包含任何传递依赖项,则该表满足第三范式。传递依赖项是指列 A 依赖于列 B,而列 B 依赖于实体键。如果您想根据 3NF 进行规范化,您需要从表中删除列 A,因为它不直接依赖于实体键,并将其放入具有自己实体键的另一个表中。
订单表
| 订单 ID | 订单日期 | 客户 ID | 客户邮政编码 |
|---|---|---|---|
| R001 | 01/17/2021 | C032 | 99702 |
| R002 | 03/01/2021 | C004 | 39204 |
| R003 | 06/30/2021 | C054 | 06505 |
| R004 | 08/22/2021 | C010 | 84098 |
| R005 | 09/27/2021 | C004 | 39204 |
此表不处于第三范式,因为客户邮政编码字段依赖于客户 ID,而客户 ID不是该表的实体键(这里的实体键是订单 ID)。我们当前的结构可能导致不希望的信息丢失;如果客户 C032 退回了他们的订单,而我们需要删除此记录,我们将无意中丢失他们的邮政编码信息。如果客户 C004 搬家并且他们的邮政编码发生了变化,我们也必须在两个地方更新它,因为他们下了多个订单。要使此表达到 3NF——您猜对了——我们将把它分解为两个表。
规范化订单表
| 订单 ID | 订单日期 | 客户 ID |
|---|---|---|
| R001 | 01/17/2021 | C032 |
| R002 | 03/01/2021 | C004 |
| R003 | 06/30/2021 | C054 |
| R004 | 08/22/2021 | C010 |
| R005 | 09/27/2021 | C004 |
规范化客户表
| 客户 ID | 客户邮政编码 |
|---|---|
| C032 | 99702 |
| C004 | 39204 |
| C054 | 06505 |
| C010 | 84098 |
规范化的缺点:何时进行非规范化
一旦达到更高的规范化级别,您的数据库在执行某些分析查询时速度可能会变慢——特别是那些需要获取大量数据的查询。由于规范化数据需要数据库访问多个表来执行查询,这可能需要更长时间,尤其是随着数据库复杂性的增加。权衡是您的规范化数据占用的空间更少。