【问题标题】:Foreign Key Constraints in OracleOracle 中的外键约束
【发布时间】:2011-08-18 23:28:19
【问题描述】:

我有实体关系模型 (ERD),其中实体 IndividualCategoryTeamCategory 与实体 Category 相关。现在我想在 Oracle DB 中创建表。我是这样开始的:

CREATE TABLE Category(
    category_id INT PRIMARY KEY,
    ...
);

CREATE TABLE Individual_category(
    category_id INT CONSTRAINT fk_cat_indivcat REFERENCES Category(category_id),
    ...,
    CONSTRAINT pk_indivgamecat PRIMARY KEY (category_id)
);

CREATE TABLE Team_category(
    category_id INT CONSTRAINT fk_cat_teamcat REFERENCES Category(category_id),
    ...,
    CONSTRAINT pk_teamcat PRIMARY KEY (category_id)
);

外键主键约束的这种组合确保了对于每个Individual_categoryCategory“超级”表(或“父“ 桌子 ?)。对于特定的Category 记录,只有一个IndividualCategory 记录。 Team_category 也一样。

为了强制继承,我还需要一个约束:确保Category 中的每条记录在IndividualCategory (X) 或TeamCategory 中都有记录,但不能同时在两者中都有一个记录。

如何创建这样的约束?


编辑:这就是我所说的“E-R 模型中的继承”。这是来自我的 DB 老师的幻灯片(他们在那里称其为“实体子类型”,但他们有时称其为继承):

【问题讨论】:

  • "inheritance" 表示分层数据,本示例不建议这样做。只是外键...
  • @OMG Ponies:我不明白你的意思......事实上,所有 3 个表中都有更多的列。 Category 中的所有列都应该继承到两个子表。但是怎么做呢?这就是问题所在。
  • 你的术语会阻碍你找到你想要的答案。没有像对象那样的“继承”——外键仅验证列中的值是否已存在于 table.column 约束所指的列中。
  • @OMG Ponies:+1 我觉得我的问题有问题,可能是术语……好吧,让我们用非常简单的话解释我的问题:我得到了 ER 模型作为任务。 Category(父)和IndividualCategoryTeamCategory(子)之间存在继承关系。 Category 实体有一些属性(例如:category_name)。 IndividualCategoryTeamCategory 都在 Category 的基础上增加了一些。我必须编写 SQL 来创建与 E-R 模型中的实体相对应的表。我该如何处理这种继承,以便我的...
  • “Entity”和“table”都是ER模型的一部分——“entity”是逻辑版本,“table”是物理版本。物理版本处理实际的列定义和数据类型大小问题。逻辑只是关于什么实体(一般会变成表格)和它们之间的关系。但是在我遇到的任何数据库设计/建模中都没有继承关系/约束——只是外键,用来展示父子关系。

标签: sql oracle database-design data-modeling foreign-key-relationship


【解决方案1】:

如何做到这一点,使用一个简化的例子:

CREATE TABLE Category(
    category_id INT PRIMARY KEY,
    category_type varchar2(300) not null,
    ...
    [list of required attributes for only individual category, but nullable],
    [list of required attributes for only team category, but nullable]
);

alter table category add constraint check_category_individual check
  (   category_type <> 'INDIVIDUAL' 
   or (    category_type = 'INDIVIDUAL' 
       and [list of individual category attributes IS NOT NULL]
      )
  );

alter table category add constraint check_category_team check
  (   category_type <> 'TEAM' 
   or (    category_type = 'TEAM' 
       and [list of team category attributes IS NOT NULL]
      )
  );

然后您可以创建视图,例如:

create view individual_category as
select category_id, [base category attributes], [individual category attributes]
  from category
 where category_type = 'INDIVIDUAL;

您甚至可以在视图上放置一个INSTEAD OF 触发器,这样应用程序就可以将其视为与其他任何表格一样的表格。

【讨论】:

    【解决方案2】:

    使用可延迟约束的完全不同的方法:

    CREATE TABLE Category(
        category_id INT PRIMARY KEY,
        team_category_id INT,
        individual_category_id INT,
        ...
    );
    
    CREATE TABLE Individual_category(
        individual_category_id INT PRIMARY KEY,
        category_id INT NOT NULL,
        ...,
    );
    
    CREATE TABLE Team_category(
        team_category_id INT PRIMARY KEY,
        category_id INT NOT NULL,
        ...,
    );
    

    确保 Category 是 TeamCategory x 或 IndividualCategory:

    alter table Category add constraint category_type_check check
      (   (team_category_id is null and individual_category_id is not null)
       or (team_category_id is not null and individual_category_id is null)
      );
    

    创建可延迟的完整性约束,以便可以在同一事务中插入类别和团队/个人类别;否则,您无法在 TeamCategory/IndividualCategory 之前插入类别,反之亦然。一条第 22 条规则。

    alter table category add constraint category_team_fk 
      foreign key (team_category_id)
        references team_category (team_category_id) 
        deferrable initially deferred;
    
    alter table category add constraint category_individual_fk 
      foreign key (individual_category_id)
        references individual_category (individual_category_id) 
        deferrable initially deferred;
    
    alter table individual_category add constraint individual_category_fk
      foreign_key (category_id) 
      references category (category_id)
      deferrable initially deferred;
    
    alter table team_category add constraint team_category_fk
      foreign_key (category_id) 
      references category (category_id)
      deferrable initially deferred;
    

    【讨论】:

    • 这似乎是迄今为止我想做的最好的方式。
    • 这可能是教授想要的,现实世界中的问题是,现在要定义类别,您必须始终加入这些表,或使用视图 - 前面的示例一直都有,并且这种解决方案实际使用起来更复杂。
    • +1 这是解决“经典”问题的有力尝试,尽管 Category 中的可空子类型键困扰了我很多。当然,问题在于 SQL(标准和实现)缺乏必要的例如CREATE ASSERTIONCHECK 约束可以包含子查询、多重赋值和理想的“不相交”约束语法(所谓的“外来分布式键”等)。
    • 为什么那些可以为空的列会困扰你?这不是我在现实世界中实现它的方式,顺便说一句 - 这是对问题的学术回答。
    【解决方案3】:

    在数据库中实现复杂约束的另一种方法是使用物化视图 (MV)。

    对于这个例子,一个 MV 可以定义如下:

    create materialized view bad_category_mv
    refresh complete on commit 
    as
    select c.category_id
    from category c
    left outer join individual_category i on i.category_id = c.category_id
    left outer join team_category i on t.category_id = c.category_id
    where (  (i.category_id is null and t.category_id is null)
          or (i.category_id is not null and t.category_id is not null)
          );
    
    alter table bad_category_mv
    add constraint bad_category_mv_chk
    check (1=0) deferrable;
    

    因此 MV 仅针对违反规则的类别填充,但随后检查约束确保任何导致 MV 中出现一行的事务都将失败(因为 1=0 永远不会为真)。

    我过去曾在博客上写过这种方法here

    注意:虽然我对这种方法很感兴趣,但我从未在生产数据库中“愤怒地”使用过它。需要仔细进行基准测试,以确保在数据更改时进行完整 MV 刷新的开销不会太高。

    【讨论】:

      【解决方案4】:

      ERD 继承是 gen-spec 设计模式的经典示例。有很多关于如何在 Oracle 等关系 DBMS 中设计 gen-spec 的文章。您可以通过在“泛化专业化关系建模”上进行谷歌搜索来找到其中的一些。

      您将在这些文章中看到的大部分内容已经在对此问题的其他回复中进行了概述。这个话题在 SO 中已经多次出现。对于先前讨论的示例,click here

      经典解决方案的主要特点是专用表有一个 id 列,它既是主键,又是引用通用表的 id 列的外键。以这种方式,子实体不会获得它们自己的身份。您真正需要注意的功能是实现析取的约束。并非所有文章都在其提出的解决方案中强制执行此规则。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多