【问题标题】:What is the best solution to design sql table relationship for a table that will be used by other tables为将由其他表使用的表设计sql表关系的最佳解决方案是什么
【发布时间】:2016-02-01 10:41:36
【问题描述】:

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

我的困境如下:

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

  1. 一种方法是使用诸如EventIdUserId 之类的列来扩展表Address。所以表EventUser 将是“父”表,地址将是“子”表。 Address 表将保存用户/事件的 Id 主键的外键。

    |EventTable:|  |UserTable: | |AddressTable|
    |           |  |           | |            |
    |EventId PK |  |UserId PK  | |AddresId PK |
    |Name       |  |Name       | |Street      |
    |OtherColumn|  |OtherColumn| |City        |
                                 |EventId FK  |
                                 |UserId FK   |
    

    我从这种设计中看到的两个缺点是,对于每一行 AddressTable 将包含额外的不必要的 Null 字段。例如,如果地址指定用户地址,则列EventId 将为空,如果地址行指定事件地址,则列UserId 将为空。

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

  2. 第二种可能性是使用Address 的主键列扩展表EventUser,以便它们成为关系中的外键。

    |EventTable:|  |UserTable: | |AddressTable|
    |           |  |           | |            |
    |EventId PK |  |UserId PK  | |AddresId PK |
    |Name       |  |Name       | |Street      |
    |OtherColumn|  |OtherColumn| |City        |
    |AddressId FK| |AddressId FK|                     
    

    除了在外键上启用级联删除时我现在有疑问之外,这个解决方案一切都将是完美的。对我来说,自然的想法是,当我删除数据库的事件或用户时,我也希望删除他们的地址。但在这样的设计中,地址表是父表,用户/事件是子表。因此,当我删除启用级联删除的地址条目时,我也会删除事件/用户条目。从逻辑上讲,这对我来说没有太大意义。应该是相反的,这是我无法解决的问题。也许第二种设计是可以接受的,我只是无缘无故地让自己感到困惑。

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

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

谢谢!

【问题讨论】:

    标签: sql sql-server entity-framework database-design table-relationships


    【解决方案1】:

    由于您给出选项 1 的原因是不可行的。

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

    一旦您计划拥有一个大地址列表,您可能希望将地址分解为它自己的层次结构:一条街道属于一个城市,一个城市属于一个国家。当然,实际上一条街道可以被几个城市共享,你可以决定在那里建立一个 n-to-n 关系,或者你可以选择 n-to-1,你有一些(但实际上非常很少)重复的街道。

    正如您所看到的,这可能会走得很远,并且会导致围绕它编写代码来管理这一切。

    另一方面,如果您对保留未使用的地址不感兴趣,则可以通过 Event 和 User 表上的删除触发器进行管理,这将检查相关地址是否已成为孤立地址,如果是,则将其删除。但是,这不应该在同一时间发生那么重要,因为您的删除操作可能需要更长的时间才能执行甚至失败,从而影响用户体验。最好异步执行此操作,并让计划的作业每周左右进行一次清理。

    【讨论】:

      【解决方案2】:

      地址确实很棘手。

      首先,地址是一个独立的东西 - 它的存在超出了您的控制范围,只要当地议会希望它存在,它就存在。另一件重要的事情 - 地址往往会一次又一次地重复使用,尤其是在我们谈论大型活动或短期租赁住宿时。

      考虑到所有这些,很明显选项 1 完全是错误的,并且与现实无关。第二个更好,但仍然错过了很多,尽管在这种情况下,它更多地取决于你愿意走多远。

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

      AddressId (PK)
      TenantId (PK)
      StartDate (PK)
      EndDate
      

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

      而这只是冰山一角:)

      【讨论】:

        【解决方案3】:

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

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

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

        【讨论】:

          【解决方案4】:

          联合表仍然是一种选择,只要您在两个 FK 上都保持唯一的约束。然而,第二个选项可能是最好的整体。为了让删除功能按照您的预期方式运行,我建议从 EventTable 和 UserTable 中设置删除触发器。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-03-13
            • 2016-12-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-09-05
            • 1970-01-01
            • 2016-01-05
            相关资源
            最近更新 更多