我在数据库设计中有一些可传递的依赖项。我的上级告诉我,这些可能会导致错误。我发现很难找到资源来告诉我拥有这些依赖关系将如何导致错误。它们会造成什么样的问题?
我并不是在争论这个事实,只是急于了解它们会引发什么样的问题。
编辑以获取更多详细信息:
来自维基百科:
传递依赖
传递依赖是一种间接函数依赖,其中X→Z仅凭借X→Y和Y→Z.
我将通过一个例子来解释:
-------------------------------------------------------------------
| Course | Field | Instructor | Instructor Phone |
-------------------------------------------------------------------
| English | Languages | John Doe | 0123456789 |
| French | Languages | John Doe | 0123456789 |
| Drawing | Art | Alan Smith | 9856321158 |
| PHP | Programming | Camella Ford | 2225558887 |
| C++ | Programming | Camella Ford | 2225558887 |
-------------------------------------------------------------------
- 如果你有一个
Course
,你可以很容易地得到它的Instructor
,所以Course->Instructor
- 如果你有一个
Instructor
,你就不能得到他的Course
,因为他可能在教不同的课程 - 如果你有一个
Instructor
,你可以很容易地得到他的Phone
,所以Instructor->Phone
这意味着,如果你有一个Course
,那么你可以得到Instructor Phone
,这意味着Course->Instructor Phone
(即传递依赖)
现在针对问题:
- 如果你同时删除
French
和English
课程,那么你也会删除他们的讲师John Doe
,他的电话号码将永远丢失 - 除非您先为他添加一个
Course
,否则无法将新的Instructor
添加到数据库中,或者您可以在Instructors table
中复制数据,这更糟 - 如果
John Doe
教练更改了他的电话号码,那么你必须用新信息更新他教授的所有课程,这可能很容易出错 - 除非删除讲师教授的所有课程或将其所有字段设置为空,否则无法从数据库中删除讲师
- 如果你决定保留导师的出生日期怎么办?您必须在
Courses
表中添加一个Birth Date
字段。这听起来合乎逻辑吗?为什么首先要在课程表中保留讲师信息
表示3NF的一种方法是:
所有属性都应该依赖于键、整个键,只依赖于键
传递依赖X->Y->Z违反了这一原则,导致数据冗余和潜在的修改异常。
让我们来分解一下:
- 根据定义,对于函数依赖X->Y->Z也是传递性,X<-Y必须而不是保持
- 如果Y是键,则X<-Y会保持,所以Y不能是一把钥匙(脚注1)
- 由于Y不是键,任何给定的Y都可以在多行中重复
- Y->Z意味着持有相同Y的所有行也必须持有相同的Z。(FOOTNOTE2)
- 在几行中重复相同的(Y,Z)元组不会为系统提供任何有用的信息。它是多余的
简而言之,由于Y不是键,并且Y->Z,我们违反了3NF。
冗余会导致修改异常(例如,更新"连接"到同一Y的Z的部分但不是全部会实质上破坏数据,因为您不再知道哪个副本是正确的)。这通常通过将原始表拆分为两个表来解决,一个表包含{X,Y},另一个表则包含{Y,Z}。这样,Y可以是第二个表中的键,Z不会重复。
另一方面,如果X<-Y确实成立(即X->Y->Z不可传递),那么我们可以保留一个表,其中X和Y都是键。在这种情况下不会不必要地重复Z。
(FOOTNOTE1)键是一组(最小)属性,在功能上决定关系中的所有属性。理由:如果K是一个键,则不可能有多行具有相同的K值,因此任何给定的K值总是与每个其他属性的一个值精确关联(假设1NF)。根据定义(参见FOOTNOTE2),"与精确的一个相关联"与"处于功能依赖关系中"是一样的
(FOOTNOTE2)根据定义,Y->Z当且仅当,每个Y值都与一个Z值精确关联
示例:
假设每条消息只有一个作者,每个作者只有一个主电子邮件,那么试图在同一个表中表示消息和用户将导致重复的电子邮件:
MESSAGE USER EMAIL
------- ---- -----
Hello. Jon jon@gmail.com
Hi, how are you? Rob rob@gmail.com
Doing fine, thanks for asking. Jon jon@gmail.com
(实际上,这些将是MESSAGE_ID
,但让我们在这里保持简单。)
现在,如果Jon决定将他的电子邮件更改为"jon2@gmail.com"?我们需要更新Jon的两行。如果我们只更新一行,那么我们会出现以下情况…
MESSAGE USER EMAIL
------- ---- -----
Hello. Jon jon2@gmail.com
Hi, how are you? Rob rob@gmail.com
Doing fine, thanks for asking. Jon jon@gmail.com
我们再也不知道乔恩的哪封电子邮件是正确的了。我们基本上已经丢失了数据!
这种情况尤其糟糕,因为没有声明性约束可以用来强迫DBMS为我们强制执行这两个更新。客户端代码将有错误,并且可能在编写时没有考虑到并发环境中可能发生的复杂交互。
但是,如果你把桌子分开。。。
MESSAGE USER
------- ----
Hello. Jon
Hi, how are you? Rob
Doing fine, thanks for asking. Jon
USER EMAIL
---- -----
Jon jon@gmail.com
Rob rob@gmail.com
现在只有一行人知道Jon的电子邮件,所以不可能含糊不清。
顺便说一句,所有这些都可以被看作是DRY原理的另一种表达。
如果表中存在可传递依赖项,则它不符合3NF;因此,表中很有可能存在冗余数据。检查此项以澄清此概念。
看看这个链接:
http://en.wikipedia.org/wiki/Transitive_dependency
举个例子,如果我在一排更新儒勒·凡尔纳的国籍,而在另一排不更新,会发生什么?作者的国籍是由作者单独决定的,而不是书和作者的结合。因此,通过示例数据结构,我可以向数据库询问儒勒·凡尔纳的国籍。如果我运行以下SQL命令
从书籍中选择排名前1的作者WHERE author="儒勒·凡尔纳"
根据数据库如何选择TOP1,我可能会得到不同的答案。