【问题标题】:Does it make sense to have three primary keys, two of which are foreign keys, in one table?在一个表中拥有三个主键(其中两个是外键)有意义吗?
【发布时间】:2012-05-24 14:33:43
【问题描述】:

我创建了一个包含三个表的数据库:

Restaurant
restaurant_id (autoincrement, PK)

Owner
owner_id (autoincrement, PK)
restaurant_id (FK to Restaurant)

Deal
deal_id (autoincrement)
owner_id (FK to Owner)
restaurant_id (FK to Restaurant)
(PK: deal_id, owner_id, restaurant_id)

每个餐厅可以有多个所有者。我为 Deal 选择了两个外键,因此我可以通过所有者或餐厅来引用该交易。交易表将有三个主键,两个是外键。它会有两个指向它的一对多关系。我所有的外键都是主键,我不知道以后会不会后悔这样做。这种设计是否有意义,并且看起来对我想要实现的目标有好处?

编辑:我真正需要在这里完成的是当所有者登录并查看他们的帐户时,我希望他们能够查看和编辑所有关联的交易与那家特定的餐厅。而且因为每家餐厅可以有多个所有者,所以我需要能够执行如下查询:select *from deal where restaurant_id = restaurant_id。换句话说,如果我是所有者并且我已登录,我需要能够进行查询:获取不仅与我(所有者)相关的所有交易,而且与与关联的所有所有者相关的所有交易这家餐厅。

【问题讨论】:

  • 如果可以从owner 表中推断出餐厅,那么就不需要deal 表中的餐厅。但我仍然会在deal 表中使用复合主键。

标签: mysql sql database-design data-modeling


【解决方案1】:

您在使用术语时遇到了一些问题。

一张表只能有一个一个主键。无法创建具有两个不同主键的表。您可以创建具有两个不同唯一索引的表(这很像一个主键),但只能存在一个主键。

您要问的是您是否应该有一个 compositecompound 主键;使用多个列的主键。

您的设计还可以,但正如您所写的那样,您可能不需要列 deal_id。在我看来,restaurant_id 和 owner_id 一起足以唯一标识 Deal 中的一行。 (如果由于资本重组或收购另一位所有者,一位所有者可以在一家餐厅拥有两种不同的所有权股份,则这可能不正确,但您在问题陈述中没有提到类似的事情)。

在这种情况下,deal_id 很大程度上浪费了存储空间。如果您有许多具有指向 Deal 的外键的表,或者如果您有要向用户显示多个 Deals 的实例,则可能需要使用 deal_id 列餐厅同时拥有。

如果其中一个论点促使您采用 deal_id 列,那么它应该是主键,并且只有它应该是主键。由于自动增量值本身是唯一的,因此包含其他两列不会添加任何内容。

【讨论】:

    【解决方案2】:

    如果你有一个唯一的字段,这应该是 PK,那将是递增的字段。
    在这种特定情况下,您根本无法向该键添加更多字段,它实际上会在一定程度上影响性能(不要问我多少,您将其替换掉)。

    【讨论】:

    • 请多考虑数据库设计,而不是默认使用合成/人工主键。如果所有三列都不允许 NULL 值,并且所有三列都需要指示唯一行——那就是主键。否则,您将查看一个主键和一个额外的唯一约束。然后考虑 MySQL(以及 SQL Server 中的主键索引)是最好的......
    【解决方案3】:

    如果您想在交易表中创建 2 个外键,即餐厅和所有者,则逻辑类似于即使没有所有者也可以存在于交易中的表,或者即使没有标识所有者也可以存在于交易中表,但您仍然可以识别该表,因为它被用作所有者表上的外键,但是如果您要将值放在定义为外键的每一列上,那么我认为这将是多余的,因为我我不知道你以后会如何使用deal table,但从它的名字来看,我认为它听起来像是用来识别餐​​厅桌子是否被客户预订,并看看你是如何设计你的数据库的即使没有在交易表中将表指定为外键,也已经识别出他们保留的表由于使用所有者表,您将能够识别他们已经保留的表,因为您将其用作所有者表上的外键你 真的必须明智地定义表之间的关系并尽可能避免冗余。 :)

    【讨论】:

      【解决方案4】:

      我认为这不是最好的。

      首先Deal table PK应该是deal_id。没有理由向它添加额外的列 - 如果您确实想在另一个表中引用 deal_id,则必须包括 restaurant_id 和 owner_id,这不好。 deal_id 是否也应该是聚集索引(也就是在此列上组织的索引)取决于数据访问模式。您的数据库中是否充满了最常用于查找的 data_id 值,还是您主要通过 owner_id 或 restaurant_id 查找交易?

      此外,使用您所描述的两个单独的 FK 方式(据我所知!)将允许交易具有无效的所有者和餐厅组合(组合不属于该所有者的所有者)餐厅)。在 Deal 表中,而不是一个 FK 到 Owner 和一个 FK 到 Restaurant,如果您必须同时拥有这两个列,则应该有一个复合 FK 到 (OwnerID, RestaurantID) 上的 Owner 表,并在 Owner 表中具有相应的唯一键允许此链接。

      但是,对于如此简单的表结构,我并没有真正看到将 RestaurantID 排除在 Deal 表之外的问题,因为 OwnerID 始终完全暗示 RestaurantID。显然,您的交易不能仅与餐厅相关联,因为这意味着 Deal:Owner 存在 1:M 关系。通过 Owner 表基于 Restaurant 进行搜索的成本应该不会那么糟糕。

      【讨论】:

        【解决方案5】:

        没有错,它有效。但是,不推荐。

        自动增量主键无需外键(或主键)即可工作

        在某些数据库中,您不能将多个字段用作单个主键。

        复合主键或组合主键在查询中更难处理。

        复合主键查询示例:

        SELECT
          D.*
        FROM 
          Restaurant AS R,
          Owner AS O,
          Deal AS D
        WHERE
          (1=1) AND
          (D.RestaurantKey = D.RestaurantKey) AND
          (D.OwnerKey = D.OwnerKey)
        

        对比

        单主键查询示例:

        SELECT
          D.*
        FROM 
          Restaurant AS R,
          Owner AS O,
          Deal AS D
        WHERE
          (D.OwnerKey = O.OwnerKey)
        

        有时,您必须将一条记录的外键值更改为另一条记录。例如,您的客户已经订购,交易记录已注册,他们决定从一张餐厅餐桌更改为另一张餐桌。因此,必须更新“所有者”和“交易”表中的数据。

        +-----------+-------------+
        |  OwnerKey | OwnerName   |
        +-----------+-------------+
        |      1    | Anne Smith  |
        +-----------+-------------+
        |      2    | John Connor |
        +-----------+-------------+
        |      3    | Mike Doe    |
        +-----------+-------------+
        
        +-----------+-------------+-------------+
        |  OwnerKey |   DealKey   |     Food    |
        +-----------+-------------+-------------+
        |      1    |       1     | Hamburguer  |
        +-----------+-------------+-------------+
        |      2    |       2     |   Hot-Dog   |
        +-----------+-------------+-------------+
        |      3    |       3     | Hamburguer  |
        +-----------+-------------+-------------+
        |      1    |       3     |    Soda     |
        +-----------+-------------+-------------+
        |      2    |       1     | Apple Pie   |
        +-----------+-------------+-------------+
        |      3    |       3     |    Chips    |
        +-----------+-------------+-------------+
        

        如果您使用复合主键,您必须为“Owner”创建新记录,为“Deals”创建新记录,复制其他字段,并删除以前的记录。

        如果你使用单键,你只需要改变Table的外键,不需要插入或删除新记录。

        干杯。

        【讨论】:

        • 原来的描述有点混乱,但是没有“Table”表。这是一个“所有者”表。请查看更新后的问题,可能会更清楚一些。
        猜你喜欢
        • 2014-11-16
        • 1970-01-01
        • 2015-02-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-01
        • 2017-05-03
        相关资源
        最近更新 更多