繁体   English   中英

如何使用Oracle声明具有OR条件的外键?

[英]How to declare a foreign key with an OR condition using Oracle?

我有一个表(A),其主键是表(B)或表(C)的外键。

create table A (
  akey number, 
  txt varchar2(10)
);

create table B (
  bkey number, 
  txt varchar2(10)
);

create table C (
  ckey number, 
  txt varchar2(10)
);

我想要的是:

alter table A add constraint BorCkey foreign key (akey) references B(bkey)` or C(ckey);

这可能吗?

外键约束是一个外表
这意味着在这种情况下你需要使用两个ALTER TABLE语句来设置外键来引用这两个表。 在那里没有机会在关系中指定OR - A.akey的值必须同时存在于B.bkey C.ckey 例如,如果B.bkey的值为NULL,但C.ckey没有 - 那么A.akey的值永远不会为NULL。 外键在Oracle中是可延迟的,但所描述的行为是在同时启用两个外键时将遇到的行为 - 如果所有值都不满足关系,则无法启用约束。

您需要检查您对如何简化关系的需求,这样就不需要两个表来完成这项工作。

不,这种事情在Oracle中是不可能的。

您的选择通常是

  • 在A中创建两个不同的列(bkey和ckey),其中bkey引用B.bkey和ckey引用C.ckey并创建一个约束,确保在任何时间点只有一个非NULL。
  • 创建某种B&C具有外键的“组合B&C”实体,并使A中的外键成为该组合实体的关键。

如果需要一个约束,以确保两列中只有一列为NULL,而对于任何行,其中一列为NOT NULL

create table one_key( 
  col1 number, 
  col2 number, 
  check( nvl2(col1,1,0) + nvl2(col2,1,0) = 1 ) 
)

听起来你有某种形式的子类型/超类型关系。 一个典型的例子是'PERSON',它可以是'CUSTOMER'或'SUPPLIER'。

您可能在PERSON表中拥有PERSON_ID的唯一键以及PERSON_TYPE('CUST'或'SUPP')的属性。 如果在PERSON_ID,PERSON_TYPE上创建主键,则可以在子类型表(SUPPLIER / CUSTOMER)中引用该主键。

然后在person_id上添加一个唯一约束,以确保person_id的任何值必须是客户或供应商,而不是两者,并检查子类型表上的约束,以便表中只表示一种类型。

create table person
  (person_id     number,
   person_type   varchar2(4),
   name          varchar2(10),
    constraint person_pk primary key (person_id, person_type),
    constraint person_id_uk unique (person_id));

create table supplier
  (supplier_id   number,
   supplier_type varchar2(4),
   blah          varchar2(10),
  constraint supplier_pk primary key (supplier_id, supplier_type),
  constraint supp_pers_fk foreign key  (supplier_id, supplier_type)
    REFERENCES person (person_id, person_type)
  )
/
alter table supplier add constraint supp_type_ck check (supplier_type = 'SUPP');

它不漂亮但类型/子类型更多的是对象概念而不是关系概念。

我的解决方案受Justin的启发:

CREATE OR REPLACE TRIGGER abc
  BEFORE INSERT OR UPDATE ON a
  FOR EACH ROW
  DECLARE
  v_testB NUMBER:= 0;
  v_testC NUMBER:= 0;
  BEGIN
    SELECT
      COUNT(bkey)
    INTO
      v_testB
    FROM
      b
    WHERE
      bkey = :new.aKey;
    SELECT
      COUNT(ckey)
    INTO
      v_testC
    FROM
      c
    WHERE
      ckey = :new.aKey;
    IF ((v_testB + v_testC) <> 1) THEN
      RAISE_APPLICATION_ERROR(-20002,'Foreign key to B or C missing.');
    END IF;
  END;
/
SHOW ERRORS TRIGGER abc

创建一个实体化视图,用于联合表B&C,并将FK约束指向视图

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM