然而,我有困难理解3.5NF或称为BCNF。以下是我所理解的:
- BCNF比3NF更严格
- 表中任何FD的左侧必须是超键(或至少是候选键)
那么为什么有些3NF表格不符合BCNF呢?我的意思是,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有所不同。
原因在于,如果Y
是X
的子集,则功能依赖关系X -> Y
显然成立。因此,在任何具有唯一候选键并且处于3NF中的表中,它已经在BCNF中,因为除了该键之外没有任何列(键或非键)是在功能上依赖于其他任何东西的。
因为每个披萨必须恰好拥有每种配料类型的其中之一,我们知道(Pizza,Topping Type)是一个候选键。我们也可以直观地知道,给定的配料不能同时属于不同的类型。因此,(Pizza,Topping)必须是唯一的,因此也是一个候选键。因此,我们有两个重叠的候选键。
我展示了一个异常情况,我们将mozarella标记为错误的配料类型。我们知道这是错的,但使其出错的规则是一种依赖关系Topping -> Topping Type
,对于此表格来说,它不是一种有效的BCNF依赖关系。这是对整个候选键之外的某些东西的一种依赖。
因此,要解决这个问题,我们将Topping Type从Pizzas表中取出,并将其作为非键属性放入Toppings表中。
您还提到:
第三范式的引用明确表示“只有键”,这意味着所有属性仅依赖于主键。
那是一种过度简化。第三范式、BCNF和所有范式都关注所有候选键和/或超键,而不仅仅是一个“主”键。
使用BCNF定义
当且仅当对于其依赖关系X → Y,至少满足以下条件之一时:
和3NF定义
当且仅当对于其每个功能依赖项X → A,至少满足以下条件之一:
而
其中
也就是说,候选键的任何非平凡子集(除了完整集)都不能依赖于任何东西,除了超键。
不符合BCNF的表/关系会面临异常情况,例如另一位用户在披萨示例中提到的更新异常。不幸的是,
目前可以在维基百科的“ 3NF表未达到BCNF(Boyce-Codd正则化形式)”中找到差异示例,其中以下表符合3NF但不符合BCNF,因为“网球场”(部分键/主属性)取决于“费率类型”(不是超键的部分键/主属性),这是我们可以通过询问数据库的客户-网球俱乐部来确定的依赖关系:
今天的网球预订 ( 3NF, not 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
第三范式问题: 部分键/主属性"Court"依赖于非超键的其他内容。相反,它依赖于部分键/主属性"Rate Type"。这意味着如果我们升级一个球场,则用户必须手动更改费率类型,或者如果想要应用费率更改则必须手动更改球场。
(从技术角度来看,我们无法保证“费率类型” -> “球场”函数依赖关系不会被违反。)
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
问题已解决: 现在,如果我们升级球场,我们可以确保费率类型将反映这种更改,并且我们不会对球场收取错误的价格。
(从技术上讲,我们可以保证功能依赖关系“费率类型” -> “球场”不会被违反。)
这是一个有价值回答的老问题,但我仍然有些困惑,直到我发现了一个真实的例子,展示了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
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
所有的答案都很好。简单来说,[BCNF]没有任何部分键可以依赖于关键字。
例如,候选键的任何非平凡子集(即除全集之外的任何子集)都不能在某些候选键上具有函数依赖性。
‘smartnut007’、‘Bill Karwin’和‘sqlvogel’的回答都很好。但是让我提出一个有趣的观点。
我们有主键和非主键。
当我们关注非主键如何依赖于主键时,我们会看到两种情况:
非主键可以依赖或不依赖。
当不依赖时:可能没有依赖或传递依赖
那么主键之间的依赖关系呢?
现在你看到了,我们既不通过第二范式也不通过第三范式来处理主键之间的依赖关系。 此外,如果有任何这样的依赖关系,则不可取,因此我们有一个单一的规则来解决这个问题。这是BCNF。
参考Bill Karwin在这里发布的示例,您将注意到‘Topping’和‘Topping Type’都是主键并具有依赖关系。如果它们是具有依赖关系的非主键,则3NF将会发挥作用。
注意:
BCNF的定义非常通用,没有区分主键和非主键属性。然而,上述思考方式有助于理解即使在第二范式和第三范式之后某些异常如何传播。
高级话题:将通用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。