2012-06-05 18 views
8

In PostgreSQL è possibile aggiungere una chiave esterna in modo condizionale?Chiave esterna PostgreSQL condizionale

Qualcosa di simile: ALTER TABLE table1 ADD FOREIGN KEY (some_id) REFERENCES other_table WHERE some_id NOT IN (0,-1) AND some_id IS NOT NULL;

In particolare, la mia tabella di riferimento ha tutti i numeri interi positivi (1+) ma il tavolo ho bisogno di aggiungere la chiave esterna per può contenere zero (0), un nullo e negativo (-1), invece, tutto ciò significa qualcosa di diverso.

Note:

sono pienamente consapevole che si tratta di una cattiva progettazione tavolo, ma era un trucco intelligente costruito 10 anni fa, quando abbiamo a disposizione, a questo punto non esistevano le caratteristiche e le risorse . Questo sistema gestisce centinaia di negozi al dettaglio, quindi tornare indietro e cambiare il metodo a questo punto potrebbe richiedere mesi che non abbiamo.

Non riesco a utilizzare un trigger, questo DEVE essere fatto con una chiave esterna.

risposta

1

È possibile aggiungere un'altra colonna "ombra" per table1 che detiene i valori puliti (vale a dire tutto, ma 0 e -1). Utilizzare questa colonna per i controlli dell'integrità referenziale. Questa colonna shadow viene aggiornata/riempita da un semplice trigger su table1 che scrive tutti i valori tranne 0 e -1 nella colonna shadow. Entrambi 0 e -1 potrebbero essere associati a null.

Quindi si dispone dell'integrità di riferimento e della colonna originale non modificata. Il rovescio della medaglia: hai anche un piccolo trigger e alcuni dati ridondanti. Ma ahimè, questo è il destino di uno schema legacy!

+0

Genio! Hai ragione che è un po 'brutto, ma il database si gestisce ancora da solo, quindi dovrebbe fare il trucco! – trex005

+0

AH, hai mai lavorato alla fonte Postgres? Questa potrebbe essere una caratteristica interessante che potresti aggiungere facendo esattamente questo dietro le quinte. – trex005

+1

@ trex005: Non ho lavorato alle origini PG. Ma ogni tanto fare il tifo sulla lista degli hacker è una mia impressione, che una tale caratteristica non sarebbe gradita. Se generalizzi il tuo esempio (abbina qualsiasi cosa tranne '0' e' -1') e pensi a una sintassi generica che copra tutti i possibili casi, otterrai un complesso sintattico piuttosto complesso. Dall'altra parte della scala: non c'è nulla che tu non possa fare con gli strumenti esistenti già. Il risultato netto: le risorse scarse (tempo/budget dello sviluppatore) pagheranno meglio su altri articoli TODO. Ma questa è solo la mia impressione. Sei libero di contattarli. –

2

La risposta breve è no, Postgres non ha condizionali chiavi esterne. Alcune opzioni che potresti prendere in considerazione sono:

  1. Non avere un vincolo FK. Spostare questa logica nel livello di accesso ai dati e vivere senza l'integrità referenziale.
  2. Consentire NULL nella colonna, che è perfettamente valido anche con un vincolo FK. Quindi, utilizzare un'altra colonna per memorizzare qualsiasi sia il significato di 0 e -1.
  3. Aggiungere una riga fittizia nella tabella di riferimento per 0 e -1. Anche se avesse solo dati fasulli, avrebbe soddisfatto il vincolo FK.

Spero che questo aiuti!

+1

questi erano più o meno le opzioni che mi aspettavo. Lascerò la domanda per un po ', nella speranza che ci sbagliamo. :) – trex005

+2

L'opzione 4 modifica sempre il codice Postgres, è * è * open source che conosci :) –

+0

molto MOLTO vero ..... qual è la tua offerta? : o) – trex005

1

la vostra esigenza è equivalente a questo vincolo di controllo:

create table t (a float check (a >= -1 and a = floor(a) or a is null)); 
+0

Vero, ma un vincolo di controllo non può controllare l'esistenza di una riga in un'altra tabella. –

+0

@MikeChristensen La domanda dice che la tabella di riferimento _ha tutti gli interi positivi (1 +) _. So che è impossibile, ma suppongo che voglia dire che ha tutti gli interi senza spazi vuoti fino a un certo limite. Quello sarebbe coperto con l'aggiunta di un'altra condizione al controllo (a <= n). –

+1

Possibile, ho appena pensato che l'OP volesse davvero una vera chiave estranea. –

0

Ecco un'altra possibilità. Usa PG Inheritance per forzare una partizione della tabella in +1 nella colonna flag e in caso contrario. (Regole/trigger usuali per mantenere questo.) Quindi avere la relazione FK tra solo la tabella figlio Has_PLUS_ONE e la tabella di riferimento.

+0

L'utilizzo dell'eredità PG non è quasi mai la soluzione giusta per nulla. Sono troppo limitati e stravaganti. – cliffordheath

0

È possibile implementare questo con un vincolo di controllo e una chiave esterna.

CREATE TABLE table1 (some_id INT, some_id_fkey INT REFERENCES other_table(other_id), CHECK (some_id IN (0,-1) OR some_id IS NOT DISTINCT FROM some_id_fkey)); 

(non testato)