复合PK的一部分外键

时间:2016-03-24 09:19:15

标签: sql oracle entity-framework

我在查找如何声明以下类型的关联时遇到问题:

说我有一张表“Weekly”:

Weekly {
Id   : Int <= PK

Week : Int
Year : Int
}

表“每月”:

Monthly{
Id   : Int <= PK

Month: Int
Year : Int
}

我还有一个“WeekMonth”表:

Monthly{
Week  : Int <= PK
Month : Int <= PK
Year  : Int <= PK
}

正如你所猜测的那样,我希望能够将Weekly与WeekMonth和Monthly与WeekMonth联系起来。

但是,我无法接合能够执行此操作:复合主键的一部分上的外键。尽管如此,在我的WeekMonth表中,年和周以及年和月字段都是显而易见的,因此它应该能够工作。

我已经尝试了多个approch来解决这个问题,但由于每月一周的自定义映射是业务需求,我对它有点困惑

2 个答案:

答案 0 :(得分:2)

  

在我的WeekMonth表中,年和周以及年和月字段都是显而易见的

事实并非如此。 '年和周'可能是唯一的,但它取决于'周'是什么 - 如果它是一个月内的一周(即1-5)那么它不是唯一的。如果它是一年中的一周(1-53)那么它是;但您在该组合上没有唯一或主键。并且“年月”并不是唯一的,因为每个组合都会有多个条目 - 4个或5个。

如果您有复合主键(或唯一键),则外键必须引用该PK中的所有列 - 否则它们不一定是唯一的。

自然钥匙在这里并不适合你。除了不允许您想要的关系外,您还要复制父表和子表中的数据。最好有一个合成密钥,例如从序列中设置:

WeekMonth{
WeekMonth_Id  : Int <= PK (synthetic, e.g. from sequence)
Week          : Int <= }
Month         : Int <= } UK
Year          : Int <= }
}

Weekly {
Weekly_Id     : Int <= PK
WeekMonth_Id  : Int <= FK to WeekMonth
}

Monthly{
Monthly_Id    : Int <= PK
WeekMonth_Id  : Int <= FK to WeekMonth
}

您不需要在子表中复制年/月/周值,因为您可以从父表中获取它们。而且你不应该复制它们,因为你不能轻易保证匹配相关的父记录,以及一般的规范化原因。

我假设你在每周和每月的表格中都有其他数据,否则它们会有点无意义;任何其他具有FK的表都可以使用FK代替WeekMonth。

如果您确实希望在子表中复制单独的年/月/周值,那么除了当前的PK之外,您还需要为这些组合使用单独的唯一键。所以你修改WeekMonth以获得一年和一月的唯一密钥(这可能取决于'周'代表什么),以及年和月的另一个唯一密钥 - 但由于这不是一个独特的组合,你不能创造那把钥匙。

答案 1 :(得分:2)

假设WeekMonth表的年度Week值为153,那么:

CREATE TABLE WeekMonth(
  Week  INT,
  Month INT,
  Year  INT,
  CONSTRAINT WeekMonth__W_M_Y__PK PRIMARY KEY ( Week, Month, Year ),
  CONSTRAINT WeekMonth__W_Y__PK UNIQUE ( Week, Year )
);

CREATE TABLE Monthly(
  ID    INT PRIMARY KEY,
  Month INT,
  Year  INT,
  FirstWeek INT GENERATED ALWAYS
    AS ( TO_NUMBER(
           TO_CHAR(
             NEXT_DAY(
               TO_DATE( month||'-'||year, 'MM-YYYY' ) - 1,
               'MONDAY'
             ),
             'WW'
           )
         )
       ),
  CONSTRAINT Monthly__M_Y__PK FOREIGN KEY ( FirstWeek, Month, Year )
    REFERENCES WeekMonth( Week, Month, Year )
);

CREATE TABLE Weekly(
  ID    INT PRIMARY KEY,
  Week  INT,
  Year  INT,
  CONSTRAINT Weekly__W_Y__PK FOREIGN KEY ( Week, Year )
    REFERENCES WeekMonth( Week, Year )
);