我目前正在建立一个具有大量多对多关系的数据库。每个关系都是通过链接表建模的。例子:
一个人有许多工作,工作由许多人完成。一个人有许多房屋,房屋被许多人占用。一个人有他喜欢的许多餐馆,餐馆有许多喜欢餐馆的人。
首先,我将其设计如下:
表格:人员,工作,房屋,餐厅,人员职位,人员房屋,人员餐厅。
关系1-n:人->人工作,人->人房子,人->人餐厅,工作->人工作,房子->人房子,餐厅->人餐厅。
这很快导致拥挤而复杂的ER模型。
为了简化此过程,我将其建模如下:
表格:人员,工作,房屋,餐厅,人员_属性
关系1-n:人员->人员属性,工作->人员属性,房屋->人员属性,餐厅->人员属性
Person_Attributes表应如下所示:
personId
jobId
houseId
restaurantId
如果存在人与工作的关系,我将添加一个类似于以下内容的条目:
P1,J1,NULL,NULL
如果存在人与房屋的关系,我将添加一个类似于以下内容的条目:
P1,NULL,H1,NULL
因此,第二个示例中的属性表将具有与第一个示例中的链接表相同的条目数。
我认为,这很大程度上简化了ER模型,并且只要我为personId + jobId,personId + houseId和personId + restaurantId建立索引,就不会对性能产生很大影响。
我的问题是:
第二种方法是对此建模的正确方法吗?如果没有,为什么?
我对性能的影响是否正确?如果没有,为什么?
我的意思是MySQL Workbench示例,可以在这里找到:
http://www.2shared.com/file/3GBnodEZ/example.html
最佳答案
您的设计违反了Fourth Normal Form。您试图将多个“事实”存储在一张表中,这会导致异常。
因此,如果我与一份工作,一所房子,但两间餐厅相关联,我是否存储以下内容?
personId jobId houseId restaurantId
1234 42 87 5678
1234 42 87 9876
如果我添加了第三家餐厅,我会复制其他列吗?
personId jobId houseId restaurantId
1234 123 87 5678
1234 123 87 9876
1234 42 87 13579
完毕!哦,等等,那里发生了什么事?我在添加新餐厅的同时更换了工作。现在,我错误地与两个作业相关联,但是无法区分这与正确地与两个作业相关联。
另外,即使与两个作业关联是正确的,数据也不应该看起来像这样吗?
personId jobId houseId restaurantId
1234 123 87 5678
1234 123 87 9876
1234 123 87 13579
1234 42 87 5678
1234 42 87 9876
1234 42 87 13579
它开始看起来像jobid,houseId和restaurantId的所有不同值的Cartesian product。实际上是因为该表正在尝试存储多个独立的事实。
正确的关系设计需要针对每个多对多关系使用单独的相交表。抱歉,您没有找到快捷方式。
(许多有关规范化的文章都说,超过3NF的较高范式是深奥的,永远不用担心4NF或5NF。让这个例子来证明这一说法。)
关于使用NULL的评论:然后,您会遇到强制唯一性的问题,因为
PRIMARY KEY
约束要求所有列都不能为NULL。personId jobId houseId restaurantId
1234 123 87 5678
1234 NULL NULL 9876
1234 NULL NULL 13579
另外,如果我在上面的表中添加了第二个房子或第二个jobId,我应该把它放在哪一行?您可能最终会这样:
personId jobId houseId restaurantId
1234 123 87 5678
1234 NULL NULL 9876
1234 42 NULL 13579
现在,如果我取消了restaurantId 9876的关联,则可以将其更新为NULL。但这留下了所有NULL的行,我真的应该删除它们。
personId jobId houseId restaurantId
1234 123 87 5678
1234 NULL NULL NULL
1234 42 NULL 13579
而如果我取消了与餐厅13579的关联,则可以将其更新为NULL,并将该行保留在原位。
personId jobId houseId restaurantId
1234 123 87 5678
1234 NULL NULL 9876
1234 42 NULL NULL
但是,如果该列中有空缺,我是否应该合并行,将jobId移至另一行?
personId jobId houseId restaurantId
1234 123 87 5678
1234 42 NULL 9876
问题是,现在添加或删除关联变得越来越复杂,需要多个SQL语句进行更改。您将不得不编写许多乏味的应用程序代码来处理这种复杂性。
但是,如果您为每个多对多关系定义一个表,则所有各种更改都很容易。 您确实需要拥有更多表的复杂性,但是这样做会简化您的应用程序代码。
向餐厅添加关联只是Person_Restaurant表中的
INSERT
。删除该关联只是DELETE
。有多少个协会与工作或房屋无关紧要。您可以在每个相交表中定义一个主键约束以强制唯一性。关于database-design - 如何使用许多:n relations on a table为数据库建模,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7083699/