2011-02-09 1 views
9

Sono alle prese con chiavi esterne nel mio DB, forse ha qualcosa a che fare con l'ereditarietà?
Quindi, ecco la configurazione di base:Chiave esterna PostgreSQL non esistente, problema dell'ereditarietà?

-- table address 
CREATE TABLE address 
(
    pk_address serial NOT NULL, 
    fk_gadmid_0 integer NOT NULL, -- this table already exists, no problem here 
    street character varying(100), 
    zip character varying(10), 
    city character varying(50), 
    public boolean, 
    CONSTRAINT address_primarykey PRIMARY KEY (pk_address), 
    CONSTRAINT gadmid_0_primarykey FOREIGN KEY (fk_gadmid_0) 
     REFERENCES adm0 (gadmid_0) MATCH SIMPLE 
     ON UPDATE CASCADE ON DELETE NO ACTION 
) 
WITH (
    OIDS=FALSE 
); 
ALTER TABLE address OWNER TO postgres; 

-- table stakeholder (parent) 
CREATE TABLE stakeholder 
(
    pk_stakeholder integer DEFAULT nextval('common_stakeholder_seq') NOT NULL, 
    fk_stakeholder_type integer NOT NULL, -- this table also exists, no problem here 
    name character varying(255) NOT NULL, 
    CONSTRAINT stakeholder_primarykey PRIMARY KEY (pk_stakeholder), 
    CONSTRAINT stakeholder_fk_stakeholder_type FOREIGN KEY (fk_stakeholder_type) 
     REFERENCES stakeholder_type (pk_stakeholder_type) MATCH SIMPLE 
     ON UPDATE CASCADE ON DELETE NO ACTION 
) 
WITH (
    OIDS=FALSE 
); 
ALTER TABLE stakeholder OWNER TO postgres; 

-- table individual (child of stakeholder) 
CREATE TABLE individual 
(
    firstname character varying(50), 
    fk_title integer, -- this table also exists, no problem here 
    email1 character varying (100), 
    email2 character varying (100), 
    phone1 character varying (50), 
    phone2 character varying (50), 
    CONSTRAINT individual_primarykey PRIMARY KEY (pk_stakeholder), 
    CONSTRAINT title_foreignkey FOREIGN KEY (fk_title) 
     REFERENCES title (pk_title) MATCH SIMPLE 
     ON UPDATE CASCADE ON DELETE CASCADE 
) INHERITS (stakeholder) 
WITH (
    OIDS=FALSE 
); 
ALTER TABLE individual OWNER TO postgres; 

-- link between stakeholder and address 
CREATE TABLE l_stakeholder_address 
(
    pk_l_stakeholder_address serial NOT NULL, 
    fk_stakeholder integer NOT NULL REFERENCES stakeholder, 
    fk_address integer NOT NULL REFERENCES address, 
    CONSTRAINT l_stakeholder_address_primarykey PRIMARY KEY (pk_l_stakeholder_address), 
    CONSTRAINT l_stakeholder_address_fk_stakeholder FOREIGN KEY (fk_stakeholder) 
     REFERENCES stakeholder (pk_stakeholder) MATCH SIMPLE 
     ON UPDATE CASCADE ON DELETE NO ACTION, 
    CONSTRAINT l_stakeholder_address_fk_address FOREIGN KEY (fk_address) 
     REFERENCES address (pk_address) MATCH SIMPLE 
     ON UPDATE CASCADE ON DELETE NO ACTION 
) 
WITH (
    OIDS=FALSE 
); 
ALTER TABLE l_stakeholder_address OWNER TO postgres; 

Finora, nessun problema. Poi ho provato ad aggiungere alcuni valori:

INSERT INTO individual (pk_stakeholder, fk_stakeholder_type, name, firstname, fk_title, email1, email2, phone1, phone2) 
    VALUES (1, 8, 'Lastname', 'Firstname', 1, '[email protected]', '', '', ''); 
INSERT INTO address (pk_address, fk_gadmid_0, street, zip, city, public) 
    VALUES (1, 126, 'Address', '', 'City', FALSE); 
INSERT INTO l_stakeholder_address (pk_l_stakeholder_address, fk_stakeholder, fk_address) 
    VALUES (DEFAULT, 1, 1); 

E finalmente finisco per avere un errore (SQL Stato 23503) dicendo che la chiave (fk_stakeholder) = (1) non è esistente nella tabella "stakeholder".
I primi 2 inserti vanno bene, li vedo nei database:

stakeholder: 
pk_stakeholder | ... 
---------------------- 
1    | ... 

address: 
pk_address | ... 
-------------------- 
1   | ... 

Che cosa sto facendo di sbagliato? Devo ammettere che sono piuttosto nuovo a PostgreSQL (usando 8.4), ma non sono nemmeno sicuro che si tratti di un problema di PG, forse mi mancano alcune comprensioni di base del database ...
In entrambi i casi, ormai ho provato praticamente tutto quello che potevo pensare, ho anche provato a rendere l'FK deferrabile come in PostgreSQL : Transaction and foreign key problem ma in qualche modo anche questo non funziona.

risposta

6

si può lavorare intorno ad esso utilizzando la tabella aggiuntiva individual_pks (individual_pk integer primary key) con tutte le chiavi primarie sia da genitore e figlio, che sarà mantenuto utilizzando i trigger (molto semplice - inserire a individual_pks su inserire, eliminare da esso su di eliminazione, aggiornalo all'aggiornamento, se cambia individual_pk).

Quindi si indicano le chiavi esterne a questa tabella aggiuntiva anziché a un figlio. Ci sarà un piccolo successo nelle prestazioni, ma solo quando si aggiungono/eliminano le righe.

Oppure dimentica l'ereditarietà e fallo alla vecchia maniera - semplicemente una tabella con alcune colonne nullable.

+0

Grazie mille per la tua risposta, sono riuscito a risolvere il mio problema in questo modo. Tuttavia, anziché "individual_pks", ho usato piuttosto "stakeholder_pk", poiché lo stakeholder è la tabella genitore. Quindi 'stakeholder_pk' raccoglie tutte le chiavi primarie di 'stakeholder' e dei suoi figli. Grazie! – Lukas

6

La tua analisi ha esattamente ragione: è a causa dell'ereditarietà. Quando si controlla la chiave esterna, le tabelle figlio non vengono considerate.

In generale, l'ereditarietà e le chiavi esterne non si combinano bene in PostgreSQL. Un grosso problema è che non è possibile avere vincoli univoci sulle tabelle.

Reference

+0

Grazie per la risposta rapida. La mia comprensione era che le tabelle figlio non contano qui perché non hanno un PK (diverso da quello ereditato) e l'FK fa riferimento alla tabella genitore (che ha il PK correttamente archiviato). Ma a quanto pare mi sbaglio (anche se non so esattamente perché). Quindi c'è un modo per fare ciò che voglio? Forse usando un trigger per verificare se fk_stakeholder nella tabella dei collegamenti esiste nel PK di stakeholder? – Lukas

+0

Le chiavi primarie non sono la preoccupazione qui. La chiave esterna fk_stakeholder punta al tavolo stakeholder, ma non vi è alcuna riga corrispondente. La riga nella tabella individui non è considerata. –

+0

A dire il vero, ancora non capisco. La chiave esterna punta a stakeholder.pk_stakeholder, che esiste perché è stata inserita dalla sua tabella figlio. Immagino che sia qui che si trova il problema - ma ancora, la fila è lì. Proprio come stakeholder_pk.pk_stakeholder è presente nell'altra soluzione di cui sopra. Suppongo che mi manchino alcune comprensioni fondamentali in questo numero. Ma non importa, non voglio occuparmi più del tuo tempo e sembra funzionare con la soluzione proposta da Tometzky. Ma molte grazie per il tuo aiuto comunque! – Lukas