2016-04-05 16 views
5

Ho una tabella di numeri di telefono di proprietà di un'azienda e una tabella di record di chiamate telefoniche. Ogni record di chiamata include numeri di origine e di destinazione (non nulli). Mi viene dato il vincolo di integrità che il numero di origine o il numero di destinazione, ma non entrambi, possono essere numeri che non sono nella tabella dei numeri di telefono (perché sono numeri non di proprietà di questa azienda). In altre parole, devo assicurarmi che almeno uno di essi sia una chiave esterna per la tabella dei numeri di telefono.Vincolo SQL: due attributi, almeno una corrispondenza di chiave esterna sulla stessa tabella

create table phonenumber (
    phonenum numeric(10,0) not null, 
    primary key (phonenum) 
); 
create table call_record (
    URID varchar(20) not null, 
    c_src numeric(10,0) not null, 
    c_dst numeric(10,0) not null, 
    primary key (URID) 
); 

I seguenti suoni come quello che voglio, ma non è valido SQL:

constraint call_constraint check (
    foreign key (c_src) references phonenumber (phonenum) or 
    foreign key (c_dst) references phonenumber (phonenum) 
) 

C'è un modo per specificare questo DDL? In caso contrario, come scriverò un trigger per far rispettare questo?

risposta

3

Modificato: Ecco un'altra idea utilizzando DDL e che non utilizzano grilletto:

create table phonenumber (
    phonenum numeric(10,0) not null, 
    primary key (phonenum) 
); 

creare una funzione per convalidare chiave esterna "a mano".

CREATE OR REPLACE FUNCTION call_check(p_src NUMBER, p_dst NUMBER) RETURN VARCHAR2 DETERMINISTIC IS 
BEGIN 
    FOR x IN (SELECT COUNT(*) c 
       FROM (SELECT 1 
         FROM phonenumber 
        WHERE phonenum = p_src 
        UNION ALL 
        SELECT 1 
         FROM phonenumber 
        WHERE phonenum = p_dst)) LOOP 
    IF x.c>=1 AND x.c <= 2 THEN 
     RETURN 'OK'; 
    END IF; 
    END LOOP; 
    RETURN 'NOK'; 
END; 

Se siete su 11g e fino, quindi aggiungere colonna virtuale e aggiungere controllo su quella colonna

--drop table call_record 
create table call_record (
    URID varchar(20) not null, 
    c_src numeric(10,0) not null, 
    c_dst numeric(10,0) not null, 
    call_check_col GENERATED ALWAYS AS (call_check(c_src, c_dst)), 
    primary key (URID) 
); 

ALTER TABLE call_record ADD CONSTRAINT call_check_con CHECK (call_check_col='OK'); 

prova di Let

SQL>  INSERT INTO phonenumber VALUES ('123'); 
1 row inserted 
SQL>  INSERT INTO call_record (urid, c_src, c_dst) VALUES ('C1', '123', '321'); 
1 row inserted 
SQL>  INSERT INTO call_record (urid, c_src, c_dst) VALUES ('C3', '123', '123'); 
1 row inserted 
SQL>  INSERT INTO call_record (urid, c_src, c_dst) VALUES ('C2', '321', '321'); 
INSERT INTO call_record (urid, c_src, c_dst) VALUES ('C2', '321', '321') 
ORA-02290: check constraint (TST.CALL_CHECK_CON) violated 
+0

Impossibile tornare a prova fino ad ora , ma questo funziona. Grazie! Sono in un corso SQL ma le colonne virtuali non sono state discusse. È un modo comune, o un modo "ideale", di imporre un vincolo come questo? Non avevo una forte preferenza per il DDL rispetto ai grilletti, ma ero bloccato in entrambi i casi. –

+0

A mio parere non è molto comune avere un vincolo come questo, questo potrebbe segnalare un problema di progettazione del modello di dati. Per qualsiasi altra preferenza sui trigger, potrei suggerire di leggere "i trigger sono cattivi" su Google. Per quanto riguarda le colonne virtuali, in realtà c'è più che puoi fare con loro, consulta la colonna Oracle Magazine 2008-March di Tom Kyte per riferimento: http://www.oracle.com/technetwork/issue-archive/2008/08-mar/o28asktom -087592.html –