我读过这句话: 数据取决于键 [1NF]、整个键 [2NF],除了键 [3NF] 什么都没有。
但是,我无法理解所谓的 3.5NF 或 BCNF。这是我的理解:
- BCNF 比 3NF 更严格
- 表中任何 FD 的左侧必须是超级键(或至少是候选键)
那么为什么有些 3NF 表不在 BCNF 中呢?我的意思是,3NF 引用明确表示“只有键”,这意味着所有属性都仅取决于主键。毕竟,主键是一个候选键,直到它被选为我们的主键。
如果到目前为止我的理解有任何问题,请纠正我并感谢您提供的任何帮助。
我读过这句话: 数据取决于键 [1NF]、整个键 [2NF],除了键 [3NF] 什么都没有。
但是,我无法理解所谓的 3.5NF 或 BCNF。这是我的理解:
那么为什么有些 3NF 表不在 BCNF 中呢?我的意思是,3NF 引用明确表示“只有键”,这意味着所有属性都仅取决于主键。毕竟,主键是一个候选键,直到它被选为我们的主键。
如果到目前为止我的理解有任何问题,请纠正我并感谢您提供的任何帮助。
您的比萨饼可以恰好有三种浇头类型:
所以我们点了两个比萨饼并选择以下配料:
Pizza Topping Topping Type
-------- ---------- -------------
1 mozzarella cheese
1 pepperoni meat
1 olives vegetable
2 mozzarella meat
2 sausage cheese
2 peppers vegetable
等一下,马苏里拉奶酪不能既是奶酪又是肉!香肠不是奶酪!
我们需要防止这些错误,让马苏里拉奶酪永远是奶酪。我们应该为此使用一个单独的表格,所以我们只在一个地方写下这个事实。
Pizza Topping
-------- ----------
1 mozzarella
1 pepperoni
1 olives
2 mozzarella
2 sausage
2 peppers
Topping Topping Type
---------- -------------
mozzarella cheese
pepperoni meat
olives vegetable
sausage meat
peppers vegetable
这是一个8岁的孩子可能理解的解释。这是更技术性的版本。
仅当存在多个重叠的候选键时,BCNF 的行为与 3NF 不同。
原因是,如果是 的子集,则函数依赖X -> Y
当然是正确的。因此,在任何只有一个候选键并且在 3NF 中的表中,它已经在 BCNF 中,因为没有列(键或非键)在功能上依赖于除该键之外的任何内容。Y
X
因为每个比萨饼必须具有每种浇头类型中的一种,所以我们知道 (Pizza, Topping Type) 是候选键。我们也直观地知道,给定的浇头不能同时属于不同的类型。所以 (Pizza, Topping) 必须是唯一的,因此也是候选键。所以我们有两个重叠的候选键。
我展示了一个异常,我们将 mozarella 标记为错误的浇头类型。我们知道这是错误的,但导致错误的规则是依赖关系Topping -> Topping Type
,它不是该表的 BCNF 的有效依赖关系。它依赖于整个候选键以外的东西。
所以为了解决这个问题,我们从 Pizzas 表中取出 Topping Type 并使其成为 Toppings 表中的非键属性。
细微的区别在于 3NF 区分了关键属性和非关键属性(也称为非主要属性),而 BCNF 没有。
最好使用Zaniolo对 3NF 的定义来解释这一点,它相当于 Codd 的:
一个关系 R 在 3NF 中,当且仅当对于 R 满足的每个非平凡 FD (X->A) 至少满足以下条件之一为真:
(a) X 是 R 的超键,或
(b) A 是 R 的关键属性
BCNF 要求 (a) 但不将 (b) 视为其自身的特殊情况。换句话说,BCNF 要求每个非平凡的行列式都是超级键,即使它的依赖属性恰好是键的一部分。
关系 R 在 BCNF 中当且仅当对于 R 满足的每个非平凡 FD (X->A) 满足以下条件:
(a) X 是 R 的超级键
因此,BCNF 更加严格。
差异是如此微妙,以至于许多人非正式地描述为 3NF 实际上是 BCNF。例如,您在此处声明 3NF 的意思是“数据取决于密钥 [s]...,而只有密钥 [s]”,但这实际上是 BCNF 而不是 3NF 的非正式描述。3NF 可以更准确地描述为“非密钥数据取决于密钥......而且只有密钥”。
你还说:
3NF 引用明确表示“只有键”,这意味着所有属性都仅取决于主键。
那是过于简单化了。3NF 和 BCNF 以及所有范式都与所有候选键和/或超级键有关,而不仅仅是一个“主”键。
使用 BCNF 定义
当且仅当对于它的每一个依赖 X → Y,至少满足以下条件之一:
和 3NF 定义
当且仅当,对于其每个函数依赖 X → A,至少满足以下条件之一:
然而
在哪里
也就是说,候选键的任何部分子集(除了完整集之外的任何非平凡子集)都不能在功能上依赖于超键以外的任何东西。
不在 BCNF 中的表/关系会出现异常,例如另一个用户在比萨示例中提到的更新异常。很遗憾,
目前可以在 Wikipedia 上的“ 3NF table not meet BCNF (Boyce–Codd normal form) ”中找到差异的示例,其中下表符合 3NF 但不符合 BCNF,因为“Tennis Court”(部分关键/主要属性)取决于关于“速率类型”(不是超键的部分键/主属性),这是一个依赖关系,我们可以通过询问数据库的客户端网球俱乐部来确定:
今天的网球场预订(3NF,不是BCNF)
Court Start Time End Time Rate Type
------- ---------- -------- ---------
1 09:30 10:30 SAVER
1 11:00 12:00 SAVER
1 14:00 15:30 STANDARD
2 10:00 11:30 PREMIUM-B
2 11:30 13:30 PREMIUM-B
2 15:00 16:30 PREMIUM-A
该表的超级键是:
S1 = {Court, Start Time}
S2 = {Court, End Time}
S3 = {Rate Type, Start Time}
S4 = {Rate Type, End Time}
S5 = {Court, Start Time, End Time}
S6 = {Rate Type, Start Time, End Time}
S7 = {Court, Rate Type, Start Time}
S8 = {Court, Rate Type, End Time}
ST = {Court, Rate Type, Start Time, End Time}, the trivial superkey
3NF 问题:部分键/主属性“Court”依赖于超键以外的其他东西。相反,它依赖于部分键/主要属性“速率类型”。这意味着如果我们升级球场,用户必须手动更改费率类型,或者如果想要应用费率更改,用户必须手动更改球场。
(从技术上讲,我们不能保证不会违反“Rate Type”->“Court”功能依赖。)
BCNF 解决方案:如果我们想将上面的表格放在 BCNF 中,我们可以将给定的关系/表格分解为以下两个关系/表格(假设我们知道费率类型仅取决于法院和会员身份,我们可以通过询问我们数据库的客户,网球俱乐部的所有者来发现):
速率类型(BCNF和较弱的 3NF,由 BCNF 隐含)
Rate Type Court Member Flag
--------- ----- -----------
SAVER 1 Yes
STANDARD 1 No
PREMIUM-A 2 Yes
PREMIUM-B 2 No
今天的网球场预订(BCNF和较弱的 3NF,由 BCNF 暗示)
Member Flag Court Start Time End Time
----------- ----- ---------- --------
Yes 1 09:30 10:30
Yes 1 11:00 12:00
No 1 14:00 15:30
No 2 10:00 11:30
No 2 11:30 13:30
Yes 2 15:00 16:30
已解决的问题:现在如果我们升级球场,我们可以保证费率类型会反映这种变化,并且我们不能对球场收取错误的价格。
(从技术上讲,我们可以保证不会违反函数依赖“Rate Type”->“Court”。)
所有好的答案。用简单的语言说[BCNF]没有部分键可以依赖于键。
即候选键的任何部分子集(即除了完整集之外的任何非平凡子集)都不能在功能上依赖于某个候选键。
'<strong>smartnut007'、'<strong>Bill Karwin' 和 '<strong>sqlvogel' 的回答非常出色。然而,让我提出一个有趣的观点。
好吧,我们有主键和非主键。
当我们关注非素数如何依赖素数时,我们会看到两种情况:
非素数可以依赖或不依赖。
不依赖时:可以有无依赖或传递依赖
素数之间的依赖关系如何?
现在你看,我们不是通过第二个或第三个 NF 来解决素数之间的依赖关系。此外,这种依赖(如果有的话)是不可取的,因此我们有一个单一的规则来解决这个问题。这是BCNF。
参考Bill Karwin在此处的帖子中的示例,您会注意到 '<em>Topping' 和 '<em>Topping Type' 都是主键并具有依赖关系。如果它们是具有依赖性的非素数,那么 3NF 就会发挥作用。
笔记:
BCNF 的定义非常通用,没有区分素数和非素数的属性。然而,上述思维方式有助于理解即使在第 2 次和第 3 次 NF 之后某些异常是如何渗透的。
高级主题:将通用 BCNF 映射到 2NF 和 3NF
现在我们知道 BCNF 提供了一个通用定义,没有参考任何素数/非素数属性,让我们看看 BCNF 和 2/3 NF 是如何相关的。
首先,BCNF 要求(除了普通情况)对于每个功能依赖X -> Y
(FD),X 应该是超级键。如果您只考虑任何 FD,那么我们有三种情况 - (1) X 和 Y 都非质数,(2) 质数和 (3) X 质数和 Y 非质数,丢弃(无意义的)情况 X 非质数-素数和 Y 素数。
对于情况(1),3NF 负责。
对于情况(3),2NF 负责。
对于情况(2),我们发现使用 BCNF
这是一个很老的问题,答案很有价值,但在找到一个显示 3NF 问题的真实示例之前,我仍然有点困惑。也许不适合 8 岁的孩子,但希望对您有所帮助。
明天我将在其中一个季度家长/教师会议上见到我大女儿的老师。这是我的日记的样子(名称和房间已更改):
Teacher | Date | Room
----------|------------------|-----
Mr Smith | 2018-12-18 18:15 | A12
Mr Jones | 2018-12-18 18:30 | B10
Ms Doe | 2018-12-18 18:45 | C21
Ms Rogers | 2018-12-18 19:00 | A08
每个房间只有一名老师,他们从不搬家。如果您看一下,您会发现: (1) 对于每个属性Teacher
, Date
, Room
,我们每行只有一个值。(2) 超级键是:和(Teacher, Date, Room)
,而候选键显然是和。(Teacher, Date)
(Date, Room)
(Teacher, Date)
(Date, Room)
(Teacher, Room)
不是超级键,因为我将在下个季度完成表格,我可能会有这样的一行(史密斯先生没有动!):
Teacher | Date | Room
---------|------------------| ----
Mr Smith | 2019-03-19 18:15 | A12
我们能得出什么结论?(1) 是 1NF 的一个非正式但正确的表述。从(2)我们看到没有“非素数属性”:2NF和3NF是免费给出的。
我的日记是3NF。好的!不。不是真的,因为没有数据建模者会在数据库模式中接受这一点。Room
属性依赖于属性Teacher
(同样:教师不动!)但模式并未反映这一事实。一个理智的数据建模师会做什么?将表一分为二:
Teacher | Date
----------|-----------------
Mr Smith | 2018-12-18 18:15
Mr Jones | 2018-12-18 18:30
Ms Doe | 2018-12-18 18:45
Ms Rogers | 2018-12-18 19:00
和
Teacher | Room
----------|-----
Mr Smith | A12
Mr Jones | B10
Ms Doe | C21
Ms Rogers | A08
但是 3NF 不处理主要属性依赖关系。这就是问题所在:在某些情况下,3NF 合规性不足以确保良好的表架构设计。
使用 BCNF,您不必关心该属性是否是 2NF 和 3NF 规则中的主要属性。对于每一个非平凡的依赖(子集显然是由它们的超集决定的),行列式是一个完整的超级键。换句话说,除了一个完整的超级密钥(不包括琐碎的 FD)之外,什么都不是由其他东西决定的。(有关正式定义,请参见其他答案)。
只要Room
取决于Teacher
,Room
必须是Teacher
(不是这种情况)的子集或Teacher
必须是超级键(我的日记中不是这种情况,但是拆分表时就是这种情况)。
总结一下:BNCF 比 3NF 更严格,但在我看来更容易掌握: