Come dichiarare una chiave esterna con una condizione OR utilizzando Oracle?
Domanda
Ho una tabella (A) la cui chiave primaria è o una chiave esterna tabella (B) o una tabella (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)
);
Quello che voglio è qualcosa di simile:
alter table A add constraint BorCkey foreign key (akey) references B(bkey)` or C(ckey);
È possibile?
Soluzione
No, questo genere di cose non è possibile in Oracle.
Le opzioni sono generalmente
- Creare due diverse colonne (tasto B e CKEY) in cui A riferimenti tasto B B.bkey e CKEY riferimenti C.ckey e creare un vincolo che assicura che solo uno è nullo in qualsiasi punto nel tempo.
- creare una sorta di "combinata B & C" entità che B & C hanno le chiavi stranieri e rendere la chiave esterna in un riferimento chiave di questa entità combinazione.
Se si desidera un vincolo che garantisce che esattamente una delle due colonne è NULL e uno non è nullo per qualsiasi riga
create table one_key(
col1 number,
col2 number,
check( nvl2(col1,1,0) + nvl2(col2,1,0) = 1 )
)
Altri suggerimenti
Un vincolo di chiave esterna è quello di una tabella esterna .
Ciò significa che avresti bisogno di utilizzare due prospetti ALTER TABLE in questa situazione per l'installazione chiavi esterne per fare riferimento le due tabelle. Non c'è alcuna possibilità in là di specificare un OR nella relazione - il valore in A.akey
avrebbe dovuto esistere sia B.bkey
e C.ckey
al tempo stesso . Ad esempio, se B.bkey
ha un valore NULL, ma non lo fa C.ckey
- A.akey
quindi non può mai avere un valore NULL. Le chiavi esterne sono differibile in Oracle, ma il comportamento descritto è ciò che si incontrano se entrambe le chiavi esterne sono abilitate allo stesso tempo - non sarà in grado di abilitare un vincolo se tutti i valori non soddisfano la relazione <. / p>
È necessario rivedere le vostre esigenze per il modo di semplificare il rapporto in modo che non ha bisogno di due tavoli per fare questo lavoro.
sembra che tu abbia una qualche forma di sottotipo / supertipo rapporto in corso. Un esempio tipico è 'persona' che può essere sia un 'cliente' o un 'FORNITORE'.
Si potrebbe avere, nella tabella PERSONA la chiave univoca di person_id più un attributo di PERSON_TYPE ( 'CUST' o 'SUPP'). Se si crea la chiave primaria sul person_id, PERSON_TYPE è possibile fare riferimento che nelle tabelle sottotipo (fornitore / cliente).
Poi si aggiunge un vincolo unico sul person_id per garantire che qualsiasi valore person_id deve essere un cliente o un fornitore, ma non entrambi, e vincoli di controllo sulle tavole sottotipo in modo che un solo tipo è rappresentato nella tabella.
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');
La sua non è bella, ma tipi / sottotipi sono più di un concetto oggetto di uno relazionale.
La mia soluzione ispirata a 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
Creare una vista materializzata che i sindacati tabelle B e C, e puntare il vincolo FK alla vista ??p>