Wie ein Fremdschlüssel mit einer OR-Bedingung mit Oracle erklären?
Frage
Ich habe eine Tabelle (A), dessen Primärschlüssels ist entweder ein Fremdschlüssel der Tabelle (B) oder eine Tabelle (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)
);
Was ich will, ist so etwas wie:
alter table A add constraint BorCkey foreign key (akey) references B(bkey)` or C(ckey);
Ist das möglich?
Lösung
Nein, so etwas ist nicht möglich, in Oracle.
Ihre Optionen sind im Allgemeinen
- Erstellen Sie zwei verschiedene Spalten (bkey und ckey) in A, wo bkey Referenzen B.bkey und ckey Referenzen C.ckey und erstellen eine Einschränkung, die sicherstellt, dass nur eine Nicht-NULL an jedem beliebigen Punkt in der Zeit.
- Erstellen Sie eine Art von „kombinierten B & C“ entity, dass B & C haben Fremdschlüssel und die Fremdschlüssel in einer Referenz machen den Schlüssel dieser Kombination Einheit.
Wenn Sie eine Einschränkung, die sicherstellt, dass genau eine von zwei Spalten ist NULL und ist NOT NULL für jede Zeile
create table one_key(
col1 number,
col2 number,
check( nvl2(col1,1,0) + nvl2(col2,1,0) = 1 )
)
Andere Tipps
Ein Fremdschlüssel ist auf eine Fremd Tabelle .
Das bedeutet müssen Sie zwei ALTE TABLE-Anweisungen in dieser Situation Setup Fremdschlüssel verwenden, um die beiden Tabellen zu verweisen. Es gibt keine Möglichkeit, dort ein OR in der Beziehung angeben - der Wert in A.akey
muß in beide B.bkey
und C.ckey
zur gleichen Zeit existieren würde . Zum Beispiel, wenn B.bkey
einen Wert von NULL hat, aber C.ckey
nicht - dann A.akey
kann niemals einen Wert von NULL hat. Fremdschlüssel sind deferrable in Oracle, aber das Verhalten beschrieben ist, was Sie begegnen, wenn beide Fremdschlüssel gleichzeitig aktiviert sind - Sie werden nicht in der Lage sein, eine Einschränkung zu aktivieren, wenn alle die Werte nicht die Beziehung <. / p>
Sie müssen Ihre Bedürfnisse überprüfen, wie die Beziehung zu vereinfachen, so dass es nicht zwei Tabellen braucht sich um diese Arbeit zu machen.
Sounds wie Sie irgendeine Form von Subtyp / geordneter Typ Beziehung geht. Ein typisches Beispiel ist ‚Person‘, die entweder ein ‚Kunde‘ oder ein ‚Lieferant‘ sein können.
Sie haben könnten, in der Person Tabelle der eindeutige Schlüssel von PERSON_ID sowie ein Attribut PERSON_TYPE ( ‚CUST‘ oder 'SUPP). Wenn Sie den Primärschlüssel auf PERSON_ID erstellen, PERSON_TYPE Sie, dass in den Subtyp Tabellen (Lieferanten- / Kunden-) verweisen können.
Dann fügen Sie eine eindeutige Einschränkung auf der person_id, um sicherzustellen, dass jeder Wert von person_id entweder ein Kunde oder Lieferant sein muss, aber nicht beide, und Check-Einschränkungen auf den Subtyp Tabellen, so dass nur ein Typ in der Tabelle dargestellt ist.
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');
Es ist nicht schön, aber Typen / Subtypen sind mehr ein Objekt Konzept als ein relationaler ein.
Meine Lösung von Justin inspiriert:
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
Erstellen Sie eine materialisierte Ansicht, dass die Gewerkschaften Tabellen B & C, und zeigen Sie Ihre FK Einschränkung der Ansicht