准备学习SQL:11. 数据库第三范式简明解释





5.00/5 (5投票s)
数据库第三范式用简单英语解释
这是关于范式化系列文章的第四篇。
第三篇文章重点介绍了第二范式、其定义以及通过示例来加深理解。
一旦一个表处于第二范式,我们就保证了每个列都依赖于主键,或者正如我喜欢说的,该表只服务于一个目的。但是列之间的关系呢?是否存在可能导致不一致的列之间的依赖关系?
一个同时包含员工年龄和出生日期的表正在制造麻烦,其中潜藏着数据不一致的机会!
如何解决这些问题?通过第三范式。
3NF – 第三范式定义
如果一个表满足以下条件,则该表处于第三范式:
- 该表处于第二范式
- 它只包含非传递依赖于主键的列
哇!这真是一大堆术语。非传递依赖是什么意思?让我们分解一下。
传递性
当某物是传递的,那么一个意义或关系在中间和整体上是相同的。如果它有帮助,可以把前缀trans理解为“跨越”。当某物是传递的,那么如果某物从开始到结束都适用,它也从中间到结束都适用。
由于十大于五,五大于三,您可以推断出十大于三。
在这种情况下,“大于”比较是传递的。通常,如果 A 大于 B,并且 B 大于 C,则 A 大于 C。
如果您很难理解“传递性”,我认为为了我们的目的,将其理解为“通过”是安全的,因为我们将审查表中的一个列如何通过第二个列与其他列相关联。
依赖性
当一个对象依赖于另一个对象时,它就对另一个对象具有依赖性。在数据库的情况下,当我们说一个列依赖于另一个列时,我们的意思是该值可以从另一个列派生。例如,我的年龄依赖于我的生日。依赖性在第二范式的定义中也起着重要作用。
传递依赖
现在,让我们将这两个词结合起来,为传递依赖制定一个我们能够理解并用于数据库列的含义。
我认为最简单的理解是,传递依赖意味着一个列的值通过第二个中间列依赖于另一个列。
考虑三列:AuthorNationality
、Author
和 Book
。AuthorNationality
和 Author
的列值依赖于 Book
;一旦知道了book
,你就可以查出Author
或AuthorNationality
。但也要注意,AuthorNationality
依赖于 Author
。也就是说,一旦你知道了 Author
,你就可以确定他们的国籍。从这个意义上讲,AuthorNationality
依赖于 Book
,通过 Author
。这就是传递依赖。
这可以概括为三列:A
、B
和 PK
。如果 A
的值依赖于 PK
,B
依赖于 PK
,并且 A
也依赖于 B
,那么你可以说 A
通过 B
依赖于 PK
。也就是说,A
传递依赖于 PK
。
让我们看一些例子来进一步理解。
主键 (PK) | 列 A | 列 B | 传递依赖? |
PersonID |
FirstName |
LastName |
否,在西方文化中,一个人的姓氏基于其父亲的 LastName ,而其 FirstName 是被赋予的。 |
PersonID |
身体质量指数 (BodyMassIndex) |
是否超重 (IsOverweight) |
是的,BMI 超过 25 被认为是超重。当 BodyMassIndex 小于 25 时,IsOverweight 的值为 true 是没有意义的。 |
PersonID |
权重 |
性别 |
否:一个人的体重和性别之间没有直接联系。 |
车辆ID (VehicleID) |
模型 |
制造商 (Manufacturer) |
是的:制造商生产特定型号。例如,福特生产嘉年华;而丰田生产凯美瑞。 |
因此,非传递依赖意味着所有列都依赖于主键(第二范式的一个标准),并且不依赖于表中的任何其他列。
我们示例数据模型的问题
让我们回顾一下我们到目前为止在数据库方面所做的工作。您会看到我发现了一个传递依赖
CustomerCity 依赖于 CustomerPostalCode,而 CustomerPostalCode 依赖于 CustomerID
一般来说,一个邮政编码适用于一个城市。尽管所有列都依赖于主键 CustomerID
,但存在更新异常的机会,因为您可以更新 CustomerPostalCode
而不对 CustomerCity
进行相应的更新。
我们已将此问题标记为红色。
根据 3NF 标准修复模型
为了使我们的模型符合第三范式,我们需要消除传递依赖。正如我们所说,我们的依赖关系是:
CustomerCity 依赖于 CustomerPostalCode,而 CustomerPostalCode 依赖于 CustomerID
CustomerPostalCode
依赖于 CustomerID
是可以的;但是,通过在表中包含 CustomerCity
,我们违反了 3NF。为了解决这个问题,我们将创建一个新表 PostalCode
,其中包含 PostalCode
作为主键,并包含 City
作为其唯一的列。
CustomerPostalCode
仍然保留在 customer
表中。然后可以将 CustomerPostalCode
指定为外键。通过这种方式,通过关系,每个客户的城市和邮政编码仍然是已知的。此外,我们消除了更新异常。
为了更好地可视化这一点,这里是带有数据的 Customer
和 PostalCode
表。
现在客户表中的每个列都依赖于主键。此外,这些列不相互依赖值。它们唯一的依赖是主键。
PostalCode
表也同样适用。
至此,我们的数据模型满足了第三范式的要求。对于大多数实际目的而言,这通常是足够的;但是,在某些情况下,还可以进一步细化数据模型。如果您对这些高级范式形式感到好奇,我建议您阅读 BCNF (Boyce-Codd 范式) 及更多内容!
范式化会失控吗?
数据库范式化是否会做得过头?当然会!有时,完全规范化数据库不值得花费时间和精力。在我们的例子中,您可以争辩说将数据库保持在第二范式,即 CustomerCity
到 CustomerPostalCode
的依赖关系并非不可接受。
我认为如果引入更新或插入异常会严重影响数据库应用程序的准确性或性能,那么您应该进行规范化。如果不是,那么请确定您是否可以依赖用户来识别并一起更新这些字段。
有时,你会故意反规范化数据。如果你需要向用户呈现摘要或已编译的数据,并且创建这些数据非常耗时或占用资源,那么单独维护这些数据可能是有意义的。
几年前,我开发了一个大型工程变更控制系统,其主页显示了每个工程师需要注意的零件、问题和任务。这是一个数据库范围内的任务列表。任务列表是使用视图实时动态重建的。性能在几年内都很好,但随着用户群的增长,每次用户访问主页时,越来越多的数据库资源被用于重建列表。
我最终不得不重新设计数据库。我用一个单独的表替换了视图,该表最初填充了视图数据,然后用代码维护以避免异常。我们需要创建复杂的应用程序代码来确保它始终是最新的。
对于用户体验来说,这是值得的。我们用处理更新异常的复杂性换取了更好的用户体验。
此文章结束了我们的范式化系列。如果您想从头开始,请点击这里。
更多教程即将推出!记住!我想提醒大家,如果您还有其他问题想得到解答,请发表评论或发推给我。我在这里帮助您。您还想了解哪些其他主题?