如何在表上建立具有许多m:n关系的数据库

Mar*_*ger 10 database-design entity-relationship relational-database database-schema

我目前正在建立一个拥有大量多对多关系的数据库.每个关系都通过链接表建模.例:

一个人有许多工作,工作由一些人完成.一个人有许多房屋,房屋被一些人占用.一个人有许多他喜欢的餐馆,餐馆有很多喜欢这家餐馆的人.

首先我设计如下:

表:人,工作,房子,餐厅,Person_Job,Person_House,Person_Restaurant.

关系1 - n:人 - > Person_Job,人 - > Person_House,人 - > Person_Restaurant,工作 - > Person_Job,House - > Person_House,Restaurant - > Person_Restaurant.

这很快导致拥挤且复杂的ER模型.

试图简化这一点我建模如下:

表格:人,工作,房子,餐厅,Person_Attributes

关系1 - n:Person - > Person_Attributes,Job - > Person_Attributes,House - > Person_Attributes,Restaurant - > 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的例子:

http://www.2shared.com/file/3GBnodEZ/example.html

Bil*_*win 20

您的设计违反了第四范式.您试图在一个表中存储多个"事实",这会导致异常.

Person_Attributes表应如下所示:personId jobId houseId restaurantId

因此,如果我与一个工作,一个房子,但两个餐馆联系,我会存储以下内容吗?

personId jobId houseId restaurantId
    1234    42      87         5678
    1234    42      87         9876
Run Code Online (Sandbox Code Playgroud)

如果我添加第三家餐馆,我会复制其他栏目吗?

personId jobId houseId restaurantId
    1234   123      87         5678
    1234   123      87         9876
    1234    42      87        13579 
Run Code Online (Sandbox Code Playgroud)

完成!哦,等等,那里发生了什么?我在添加新餐厅的同时更换了工作.现在我错误地与两个工作相关联,但没有办法区分它并正确地与两个工作相关联.

此外,即使与两个作业关联是正确的,数据是否应该如下所示?

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 
Run Code Online (Sandbox Code Playgroud)

它开始看起来像jobId,houseId和restaurantId的所有不同值的笛卡尔积.事实上,它是 - 因为这个表试图存储多个独立的事实.

正确的关系设计需要为每个多对多关系提供单独的交集表.对不起,您还没找到快捷方式.

(关于规范化的许多文章都表示,过去3NF的较高正常形式是深奥的,人们不必担心4NF或5NF.让这个例子反驳这种说法.)


关于使用NULL的评论:然后你有一个强制执行唯一性的问题,因为PRIMARY KEY约束要求所有列都是NOT NULL.

personId jobId houseId restaurantId
    1234   123      87         5678
    1234  NULL    NULL         9876
    1234  NULL    NULL        13579 
Run Code Online (Sandbox Code Playgroud)

另外,如果我在上表中添加第二个house或第二个jobId,我将它放入哪一行?你最终可能会这样:

personId jobId houseId restaurantId
    1234   123      87         5678
    1234  NULL    NULL         9876
    1234    42    NULL        13579 
Run Code Online (Sandbox Code Playgroud)

现在,如果我取消关联restaurantId 9876,我可以将其更新为NULL.但是这留下了所有NULL的一行,我真的应该删除它.

personId jobId houseId restaurantId
    1234   123      87         5678
    1234  NULL    NULL         NULL
    1234    42    NULL        13579 
Run Code Online (Sandbox Code Playgroud)

然而,如果我将餐厅13579解除关联,我可以将其更新为NULL并将该行留在原位.

personId jobId houseId restaurantId
    1234   123      87         5678
    1234  NULL    NULL         9876
    1234    42    NULL         NULL 
Run Code Online (Sandbox Code Playgroud)

但是,我不应该合并行,将jobId移动到另一行,前提是该列中有空位吗?

personId jobId houseId restaurantId
    1234   123      87         5678
    1234    42    NULL         9876
Run Code Online (Sandbox Code Playgroud)

问题是,现在添加或删除关联变得越来越复杂,需要多个SQL语句进行更改.您将不得不编写大量繁琐的应用程序代码来处理这种复杂性.

但是,如果您按照多对多关系定义一个表,则所有各种更改都很容易. 您确实需要拥有更多表的复杂性,但通过这样做,您将简化应用程序代码.

向餐馆添加关联只是INSERTPerson_Restaurant表的一个关联.删除该关联只是一个DELETE.无论工作或房屋有多少协会都无关紧要.您可以在每个交集表中定义主键约束以强制实现唯一性.

  • 您对4NF的评论+1不是深奥的 (3认同)