【问题标题】:Foreign key referring to primary keys across multiple tables?外键是指跨多个表的主键?
【发布时间】:2009-03-21 07:36:42
【问题描述】:

我有两个表,即employees_ce 和employees_sn 数据库employees 下。

它们都有各自唯一的主键列。

我有另一个名为 deductions 的表,我想引用它的外键列来引用employees_ce 和employees_sn 的主键。这可能吗?

例如

employees_ce
--------------
empid   name
khce1   prince

employees_sn
----------------
empid   name
khsn1   princess

这可能吗?

deductions
--------------
id      name
khce1   gold
khsn1   silver

【问题讨论】:

    标签: sql foreign-keys primary-key


    【解决方案1】:

    假设我正确理解了您的情况,这就是我所说的正确方法:

    从更高级别的数据库描述开始!您有员工,员工可以是“ce”员工和“sn”员工(无论是什么)。在面向对象的术语中,有一个类“employee”,它有两个子类,分别称为“ce employee”和“sn employee”。

    然后您将这个更高级别的描述转换为三个表:employeesemployees_ceemployees_sn

    • employees(id, name)
    • employees_ce(id, ce-specific stuff)
    • employees_sn(id, sn-specific stuff)

    由于所有员工都是员工(呃!),每个员工在employees 表中都会有一行。 “ce”员工在employees_ce 表中也有一行,“sn”员工在employees_sn 表中也有一行。 employees_ce.idemployees.id 的外键,就像 employees_sn.id 一样。

    要提及任何类型的员工(ce 或 sn),请参阅employees 表。也就是说,您遇到问题的外键应该引用该表!

    【讨论】:

    • 如何让ce和sn互斥?由于员工不能同时是 ce 和 sn,因此最好将其反映在数据库中。我现在遇到了这个问题。
    • 我认为多个列键可以帮助解决我之前评论中的问题...现在就查找。
    • 您可以通过将类型存储在基表和派生表中来强制员工仅在一个表中(以及正确的一个表中)。使主键 id,唯一键在 (id,type) 上,子表的外键在 (id, type) 上,并在每个子表上放置一个 CHECK 约束,使其只有正确的类型。或者,如果您的数据库执行全局检查约束(并且没有巨大的速度损失),您当然可以只进行 NOT EXISTS 检查。
    • 检查 this Answer 以获得完整的解释和实施细节。
    • 如何知道具有特定 id 的员工是 'se' 还是 'sn' ?
    【解决方案2】:

    您可能可以添加两个外键约束(老实说:我从未尝试过),但它会坚持父行存在于两个表中。

    相反,您可能希望为您的两个员工子类型创建一个超类型,然后将外键指向那里。 (当然,假设您有充分的理由将这两种员工分开)。

                     employee       
    employees_ce     ————————       employees_sn
    ————————————     type           ————————————
    empid —————————> empid <——————— empid
    name               /|\          name
                        |  
                        |  
          deductions    |  
          ——————————    |  
          empid ————————+  
          name
    

    员工表中的type 将是cesn

    【讨论】:

    • 我尝试添加多个外键,它们起作用了,但是在添加记录时,java derby 告诉我两个外键约束都被违反了!
    • 我刚刚在 PostgreSQL 上试过,它在那里工作。您在两个表中都有父记录吗?
    • 父记录你的意思是,empid?
    • 问题肯定已经从“deductions”表转移到“employee”表了。您将如何根据类型引用可能不同的实体?
    • @gawpertron:嗯,empid 在所有类型中都是独一无二的。您可以使用“类型”字段来查看需要引用的子表。或者只是LEFT JOIN所有这些,如果数量足够少的话。当不使用 'employee' 基表时,无法声明主键(因为它正在引用 tableA 或 tableB 或……);现在可以了。假设拆分 employees_ceemployees_sn 是明智的,并记录了该假设。
    【解决方案3】:

    其实我自己就是这么做的。我有一个名为“评论”的表,其中包含其他 3 个表中记录的 cmets。这两种解决方案实际上都无法处理您可能想要的所有内容。在你的情况下,你会这样做:

    解决方案 1:

    1. 在employees_ce 和employees_sn 中添加一个tinyint 字段,该字段的默认值在每个表中都不同(该字段表示“表标识符”,因此我们将它们称为tid_ce & tid_sn)

    2. 使用表的 PK 和表 id 字段在每个表上创建唯一索引。

    3. 向您的“扣除”表添加一个 tinyint 字段以存储外键的后半部分(表 ID)

    4. 在您的“扣除”表中创建 2 个外键(您不能强制执行参照完整性,因为一个键将是有效的,或者另一个键是有效的......但绝不是两者:

      ALTER TABLE [dbo].[Deductions]  WITH NOCHECK ADD  CONSTRAINT [FK_Deductions_employees_ce] FOREIGN KEY([id], [fk_tid])
      REFERENCES [dbo].[employees_ce] ([empid], [tid])
      NOT FOR REPLICATION 
      GO
      ALTER TABLE [dbo].[Deductions] NOCHECK CONSTRAINT [FK_600_WorkComments_employees_ce]
      GO
      ALTER TABLE [dbo].[Deductions]  WITH NOCHECK ADD  CONSTRAINT [FK_Deductions_employees_sn] FOREIGN KEY([id], [fk_tid])
      REFERENCES [dbo].[employees_sn] ([empid], [tid])
      NOT FOR REPLICATION 
      GO
      ALTER TABLE [dbo].[Deductions] NOCHECK CONSTRAINT [FK_600_WorkComments_employees_sn]
      GO
      
      employees_ce
      --------------
      empid    name     tid
      khce1   prince    1
      
      employees_sn
      ----------------
      empid    name     tid 
      khsn1   princess  2
      
      deductions
      ----------------------
      id      tid       name  
      khce1   1         gold
      khsn1   2         silver         
      ** id + tid creates a unique index **
      

    解决方案 2: 此解决方案允许维护参照完整性: 1.在'Deductions'表中创建第二个外键字段,两个外键都允许Null值,并创建普通外键:

        employees_ce
        --------------
        empid   name
        khce1   prince 
    
        employees_sn
        ----------------
        empid   name     
        khsn1   princess 
    
        deductions
        ----------------------
        idce    idsn      name  
        khce1   *NULL*    gold
        *NULL*  khsn1     silver         
    

    仅当列不为空时才检查完整性,因此您可以保持引用完整性。

    【讨论】:

      【解决方案4】:

      我知道这是一个长期停滞的话题,但如果有人在这里搜索,我会如何处理多表外键。使用这种技术,您没有任何 DBA 强制级联操作,因此请确保您在代码中处理 DELETE 等。

      Table 1 Fruit
      pk_fruitid, name
      1, apple
      2, pear
      
      Table 2 Meat
      Pk_meatid, name
      1, beef
      2, chicken
      
      Table 3 Entity's
      PK_entityid, anme
      1, fruit
      2, meat
      3, desert
      
      Table 4 Basket (Table using fk_s)
      PK_basketid, fk_entityid, pseudo_entityrow
      1, 2, 2 (Chicken - entity denotes meat table, pseudokey denotes row in indictaed table)
      2, 1, 1 (Apple)
      3, 1, 2 (pear)
      4, 3, 1 (cheesecake)
      

      SO Op 的示例如下所示

      deductions
      --------------
      type    id      name
      1      khce1   gold
      2      khsn1   silver
      
      types
      ---------------------
      1 employees_ce
      2 employees_sn
      

      【讨论】:

        【解决方案5】:

        技术上可行。您可能会在扣减和employees_sn 中引用employees_ce。但是为什么不合并employees_sn 和employees_ce?我看不出你有两张桌子的理由。没有一对多的关系。并且(在这个例子中没有)很多列。

        如果您对一列进行两次引用,则员工必须在两个表中都有一个条目。

        【讨论】:

          【解决方案6】:

          是的,这是可能的。您将需要为第三张桌子定义 2 个 FK。每个 FK 指向一个表的必填字段(即每个外部表 1 个 FK)。

          【讨论】:

            【解决方案7】:

            假设由于某种原因您必须为两种员工类型提供两个表,我将扩展 vmarquez 的答案:

            架构:

            employees_ce (id, name)
            employees_sn (id, name)
            deductions (id, parentId, parentType, name)
            

            扣除数据:

            deductions table
            id      parentId      parentType      name
            1       1             ce              gold
            2       1             sn              silver
            3       2             sn              wood
            ...
            

            这将允许您将扣除指向架构中的任何其他表。数据库级约束 IIRC 不支持这种关系,因此您必须确保您的应用程序正确管理约束(如果您有多个不同的应用程序/服务访问同一个数据库,这会变得更加麻烦)。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2017-01-09
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-03-25
              • 2018-11-20
              相关资源
              最近更新 更多