【问题标题】:Multiple foreign keys to a single column单个列的多个外键
【发布时间】:2010-11-14 06:55:56
【问题描述】:

我正在为客户/订单系统定义一个数据库,其中有两种高度不同的客户类型。因为它们是如此不同,所以只有一个客户表会非常难看(它会充满空列,因为它们对于一种类型毫无意义)。

虽然他们的订单格式相同。是否可以在我的 Order 表中有一个 CustomerId 列,它对两种客户类型都有一个外键?我已经在 SQL Server 中设置了它,它没有给我创建关系的问题,但我还没有尝试插入任何数据。

另外,我打算使用 nHibernate 作为 ORM,这样的关系会不会引入任何问题?

【问题讨论】:

  • 你希望外援们做什么?您不清楚表中对 CustomerId 列的任何限制。所以不清楚哪些约束适合声明。

标签: sql sql-server nhibernate database-design


【解决方案1】:

不,您不能将单个字段作为两个不同表的外键。你怎么知道在哪里寻找钥匙?

您至少需要一个说明用户类型的字段,或者两个单独的外键。

您还可以将所有用户共有的信息放在一个表中,并为特定于用户类型的信息设置单独的表,这样您就有一个以用户 ID 作为主键的表。

【讨论】:

  • 同意。规范化是这里的关键。
  • @Guffa: -1 因为“不,您不能将单个字段作为两个不同表的外键” - 此语句不正确(至少在 sql server 2005 上)。试一试。
  • @Liao:我不认为你了解情况......如果你的外键值为42,你怎么知道它是表A还是表B中的键?
  • 我了解实现结果的规范化/桥接方面,但要回答这个问题......如果允许,它将是表 A 和表 B 中的关键。这就是重点.我很难理解为什么有人会这样做,但我也不明白为什么如果你想这样做是不可能的,例如(糟糕的设计)制作一个 User 表和一个 UserMetadata 表都使用 UserID 作为键。真正的 UserID 应该是用户的 FK,或者用户表中的 MetadataID。但是......就技术原因而言,SQL 应该支持糟糕的设计决策......至少这是我的观点。我宁愿不受限制。
  • FK 也会强制创建元数据。例如,用户可以注册并稍后输入他们的元数据。 “双 FK”可能是一个奇怪的数据库技巧,说“在生成元数据之前不要让条目放入此表中”,因为如果字段不为空,则无法满足第二个 FK?
【解决方案2】:

外键只能引用一个主键,所以不能。但是,您可以使用桥接表:

CustomerA <---- CustomerA_Orders ----> Order
CustomerB <---- CustomerB_Orders ----> Order

所以 Order 甚至没有 外键;这是否是可取的,虽然......

【讨论】:

    【解决方案3】:

    我继承了一个完成此操作的 SQL Server 数据库(在四个外键关系中使用的单个列与四个不相关的表),所以是的,这是可能的。不过,我的前任已经走了,所以我不能问他为什么认为这是个好主意。

    他使用了一个 GUID 列(“uniqueidentifier”类型)来避免歧义问题,并且他关闭了对外键的约束检查,因为它保证只有一个会匹配。但我能想到很多你不应该的理由,我也没想到你应该的任何理由。

    您的问题听起来确实像经典的“专业化”问题,通常通过创建一个包含共享客户数据的父表,然后创建两个包含每个客户类别独有数据的子表来解决。然后,您的外键将针对父客户表,您将根据哪个子表具有匹配条目来确定哪种类型的客户。

    【讨论】:

      【解决方案4】:

      您可以创建引用多个表的外键。此功能允许对表进行垂直分区并仍保持引用完整性。但是,在您的情况下,这不适用。

      最好的办法是创建一个包含可能列的 CustomerType 表 - CustomerTypeID、CustomerID,其中 CustomerID 是 PK,然后将您的 OrderID 表引用到 CustomerID。

      拉吉

      【讨论】:

        【解决方案5】:

        如前所述,如果密钥是 12345,您如何知道在哪个表中查找它?我想,您可以做一些事情来确保两个表的键值永远不会重叠,但这太难看和太痛苦了。您可以有第二个字段来说明它是哪种客户类型。但是,如果您将有两个字段,为什么不为客户类型 1 id 设置一个字段,为客户类型 2 设置另一个字段。

        在不了解您的应用的情况下,我的第一个想法是,您确实应该有一个通用客户表,其中包含两者共有的数据,然后再有两个额外的表,其中包含特定于每种客户类型的数据。我认为这两者肯定有很多共同的数据——至少像姓名和地址以及客户编号这样的基本数据——并且跨表重复列很浪费时间。然后附加表可以参考基表。由于基表只有一个键,外键必须知道要引用哪个表的问题就消失了。

        【讨论】:

          【解决方案6】:

          两种不同类型的客户是类型和子类型的经典案例,或者,如果您愿意,也可以是类和子类。 Here 是另一个问题的答案。

          本质上,类表继承技术就像 Arnand 的答案。使用共享主键技术可以让您在一个列中解决由两种类型的外键产生的问题。外键将是客户 ID。这将识别客户表中的一行,以及适当类型的客户类型表中的一行,视情况而定。

          【讨论】:

            【解决方案7】:

            我知道这是一个非常古老的问题;但是,如果其他人通过 google 找到这个问题,并且您不介意在表格中添加一些列,那么我使用的一种技术(使用原始问题作为假设问题来解决)是:

            1. 添加 [CustomerType] 列。在此处存储值的目的是指示哪个表包含您的(假定的)[CustomerId] FK 列的 PK。可选 - 添加检查约束(以确保 CustomerType 在 CustomerA 或 CustomerB 中)将帮助您在晚上睡得更好。

            2. 为每个 [CustomerType] 添加一个计算列,例如:
              [CustomerTypeAId] as case when [CustomerType] = 'CustomerA' then [CustomerId] end persisted
              [CustomerTypeBId] as case when [CustomerType] = 'CustomerB' then [CustomerId] end persisted

            3. 将您的外键添加到计算(和持久)列中。

            警告:我主要是在 MSSQL 环境中;所以我不知道这对其他 DBMS(即:Postgres、ORACLE 等)的转化效果如何。

            【讨论】:

              【解决方案8】:
              1. 创建一个“客户”表,其中包含对于两种类型的客户具有相同数据的所有列。
              2. 比创建表“customer_a”和“customer_b”
              3. 使用“consumer”表中的“customer_id”作为“customer_a”和“customer_b”的外键

                                customer
                                    |
                     ---------------------------------
                     |                               |
                cusomter_a                      customer_b
                

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-08-03
                • 1970-01-01
                • 2013-11-13
                • 2020-03-30
                • 1970-01-01
                • 2014-05-31
                相关资源
                最近更新 更多