为其他表使用的表设计sql表关系的最佳解决方案是什么

为其他表使用的表设计sql表关系的最佳解决方案是什么,sql,sql-server,entity-framework,database-design,table-relationships,Sql,Sql Server,Entity Framework,Database Design,Table Relationships,我遇到了一个相当有趣的情况,我需要指导来帮助我设计遵循“最佳实践”或“推荐方式”的数据库模式 我的困境如下: 我有一个事件表,其中包含Id、姓名、日期等基本属性。它需要地址信息,因此最直接的方法是使用街道、城市、国家等字段扩展表。我还有一个用户表,也需要存储地址数据。所以正确的做法是创建第三个名为Address的表,并在Address/User和Address/Event之间建立关系。这是棘手的部分。哪个表应该包含主键/外键 一种方法是使用诸如EventId和UserId等列扩展表Address

我遇到了一个相当有趣的情况,我需要指导来帮助我设计遵循“最佳实践”或“推荐方式”的数据库模式

我的困境如下:

我有一个
事件
表,其中包含Id、姓名、日期等基本属性。它需要地址信息,因此最直接的方法是使用街道、城市、国家等字段扩展表。我还有一个用户表,也需要存储地址数据。所以正确的做法是创建第三个名为Address的表,并在Address/User和Address/Event之间建立关系。这是棘手的部分。哪个表应该包含主键/外键

  • 一种方法是使用诸如
    EventId
    UserId
    等列扩展表
    Address
    。因此,表
    Event
    User
    将是“父”表,而address将是“子”表。
    地址表将保存用户/事件Id主键的外键

    |EventTable:|  |UserTable: | |AddressTable|
    |           |  |           | |            |
    |EventId PK |  |UserId PK  | |AddresId PK |
    |Name       |  |Name       | |Street      |
    |OtherColumn|  |OtherColumn| |City        |
                                 |EventId FK  |
                                 |UserId FK   |
    
    我从这种设计中看到的两个缺点是,对于每一行
    AddressTable
    ,都会包含额外的不必要的空字段。例如,如果地址指定了用户地址,则列
    EventId
    将为Null;如果地址行指定了事件地址,则列
    UserId
    将为Null

    第二个缺点是,每当我添加一个也需要连接到地址表的新表时,我都需要向表地址添加另一列,该列将引用新表的主键

  • 第二种可能性是使用
    Address
    的主键列扩展表
    Event
    User
    ,使它们成为关系中的外键

    |EventTable:|  |UserTable: | |AddressTable|
    |           |  |           | |            |
    |EventId PK |  |UserId PK  | |AddresId PK |
    |Name       |  |Name       | |Street      |
    |OtherColumn|  |OtherColumn| |City        |
    |AddressId FK| |AddressId FK|                     
    
    使用这个解决方案一切都会很完美,除了我现在对启用外键上的级联删除有疑问。对我来说,一种自然的想法是,当我删除数据库的事件或用户时,我也希望删除他们的地址。但在这种设计中,地址表是父级,用户/事件是子级。所以,当我删除启用级联删除的地址条目时,我也会删除事件/用户条目。从逻辑上讲,这对我来说没什么意义。应该是相反的,这是我无法解决的问题。也许第二种设计是可以接受的,我只是无缘无故地把自己搞糊涂了

  • 理想情况下,我很想提出这样的设计,通过启用级联删除,我首先删除事件或用户,然后自动删除他们的地址

    我知道联合表有第三种选择,但这只适用于多对多关系,如果用户/事件只包含一个地址会怎么样


    谢谢

    只要在两个FK上保持唯一的约束,关节表仍然是一个选项。然而,第二种选择可能是总体上最好的。为了让deletes按您想要的方式运行,我建议在从EventTable和UserTable删除时设置一个触发器。

    第二种方法对我来说似乎是最干净的。毕竟,例如,您可能有多个具有相同地址的用户。然而,我应该指出,“事件”地址和“个人”地址是否相同并不清楚。例如,“个人”地址可能有邮政编码,“事件”地址可能描述到达某一地点的不同方式

    在任何情况下,您都有反向级联。例如,当你删除一个用户时,你是在向后思考。地址不会发生任何变化。问题是当你从地址中删除某些内容时会发生什么。然后,相应的用户和事件将被删除。级联的目的是在主键更改时保持关系完整性


    如果您想在删除用户/事件时删除地址,那么我建议使用触发器。然而,这对于关系完整性来说并不是必需的。

    地址确实很棘手

    首先,地址是一个独立的东西——它的存在超出了你的控制,相反,只要地方议会愿意,它就存在。另一件重要的事情——地址往往会被反复使用,特别是当我们谈论大型活动或短期租赁住宿时

    考虑到所有这些,选择1显然是错误的,与现实不相关。第二个更好,但仍然错过了很多,虽然在这种情况下,它更多地取决于你愿意走多远

    例如,如果要存储任何类型实体的地址更改历史记录,则需要历史记录表-同样,有几种可能的设计。您可以使用以下字段创建一个地址历史记录表:

    AddressId (PK)
    TenantId (PK)
    StartDate (PK)
    EndDate
    
    ,其中,
    TenantId
    将引用一个超类型表,该表将成为所有可以使用地址的实体的父级。这样一个表(不是超类型表)也将有助于防止(或允许?)在任何给定时间多个租户同时使用同一地址


    这只是冰山一角:)

    因为你给出的理由,选项1是不可能的

    使用选项2,您不必担心未使用的地址记录。事实上,它们在创建新事件或用户时可能会变得有用,因为您可以在地址“数据库”中提供搜索功能。进一步说,您甚至可以决定使用从某个地址提供者下载的数据预填充地址表。那么搜索工具就会变得非常有用

    一旦你计划有一个大的地址列表,你可能想把一个地址分成它自己的等级:一条街道属于一个城市,一个城市属于一个国家。当然,在实践中,一条街道可以由多个城市共享,您可以决定建立n对n关系