我目前正在建立一个拥有大量多对多关系的数据库。每个关系都通过链接表建模。例如:
一个人有很多工作,一些人完成了工作。一个人有许多房屋,房屋被一些人占用。一个人有很多他喜欢的餐馆,餐馆里有很多喜欢这家餐馆的人。
首先,我将其设计如下:
表:人,工作,住宅,餐厅,Person_Job,Person_House,Person_Restaurant。
关系1 - n:人 - > Person_Job,Person - > Person_House,Person - > Person_Restaurant,工作 - > Person_Job,House - > Person_House,餐厅 - > Person_Restaurant。
这很快导致拥挤而复杂的ER模型。
尝试简化此操作我将其建模如下:
表格:人,工作,房屋,餐厅,人物_属性
关系1 - n:人 - > Person_Attributes,Job - > Person_Attributes,House - > Person_Attributes,餐厅 - > Person_Attributes
Person_Attributes表应该如下所示: PERSONID 的jobId houseId restaurantId
如果存在人 - 工作关系,我将添加一个类似于以下内容的条目:
P1,J1,NULL,NULL
如果存在人与人之间的关系,我将添加一个类似于以下内容的条目:
P1,NULL,H1,NULL
因此,第二个示例中的属性表将与添加的第一个示例的链接表具有相同数量的条目。
这简单地简化了ER模型,只要我为personId + jobId,personId + houseId和personId + restaurantId构建索引,我认为不会对性能造成太大影响。
我的问题是: 第二种方法是一种正确的建模方法吗?如果没有,为什么? 我对性能影响是对的吗?如果没有,为什么?
我在这里可以找到MySQL Workbench的例子:答案 0 :(得分:20)
您的设计违反Fourth Normal Form。您试图在一个表中存储多个“事实”,这会导致异常。
Person_Attributes表应如下所示:personId jobId houseId restaurantId
因此,如果我与一份工作,一所房子,但两家餐馆联系,我会存储以下内容吗?
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
另外,如果我在上表中添加第二个house或第二个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
。无论工作或房屋有多少协会都无关紧要。您可以在每个交集表中定义主键约束以强制实现唯一性。
答案 1 :(得分:2)
您的简化版本并不代表正确的关系模型。它更像是元数据模型。
数据库中的表数应表示域中逻辑实体的数量。这不应该根据有多少实体太多的任意想法而改变。
答案 2 :(得分:2)
我不认为第二种方法是正确的,因为Person_Attributes表将包含冗余数据。例如: 比如一个人喜欢10家餐馆并从事2个工作岗位,有3个房子你会有多达10 * 2 * 3个参赛作品应该是10 + 2 + 3(在3个链接表中......根据方法#1) 。想想有百万用户的缺点,如果你在Person_Attributes表中有超过3个属性来处理...... 所以我会在你的问题中采用方法1。
比如说你的Person_Attributes表有以下条目:
personId | houseId | jobId | restaurantId
------------------------------------------
P1 H1 J1 R1
现在,如果这个人喜欢餐馆R2和R3 ...表格看起来像
P1 H1 J1 R1
P2 H1 J1 R2
P2 H1 J1 R3
表已经有冗余数据了 他稍后补充了Job J2。 你的桌子看起来像
P1 H1 J1 R1
P2 H1 J1 R2
P2 H1 J1 R3
P1 H1 J2 R1
P2 H1 J2 R2
P2 H1 J2 R3
现在考虑他增加了另一个家庭H2 ......等等......你明白我的意思吗?
答案 3 :(得分:1)
以我的拙见,我会选择第一个模型。它可能是一个更复杂的模型,但最终它会使您从表中提取信息时更容易,并且应用程序代码可能会变得更脏或更难以被其他程序员读取。此外,还有一些作者不会建议使用这样的多用途表。
最后,你必须选择最适合自己的东西。我们不了解整个背景,因此无法帮助您做出太多决定。但是,就你所说的而言,我肯定会选择第一选项。
答案 4 :(得分:1)
第二个模型从几个角度来看是一个问题。首先,它很可能会创建阻塞问题,因为一切都进入了一个元表。其次,由于您无法执行外键约束,因此更有可能出现数据完整性问题。它是一种以这种方式建模的SQL反模式。第一个模型是正确的。