2012-06-05 17 views
8

Ist es in PostgreSQL möglich, einen Fremdschlüssel bedingt hinzuzufügen?Bedingter PostgreSQL-Fremdschlüssel

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

Insbesondere hat meine Referenztabelle alle positiven ganzen Zahlen (1+) aber die Tabelle Ich brauche die Fremdschlüssel hinzufügen enthalten Null (0), null und negative (-1) stattdessen alle etwas anderes bedeuten.

Hinweise:

bin ich voll und ganz bewusst, dass diese schlechte Tabellenentwurf ist, aber es war ein kluger Trick 10+ Jahren gebaut, als die Funktionen und Ressourcen, die wir an dieser Stelle zur Verfügung haben, nicht existieren . Dieses System betreibt Hunderte von Einzelhandelsgeschäften, so dass das Zurückgehen und Ändern der Methode zu diesem Zeitpunkt Monate dauern könnte, die wir nicht haben.

Ich kann keinen Trigger verwenden, dies muss mit einem Fremdschlüssel getan werden.

Antwort

1

Sie einen anderen „Schatten“ Spalte table1 hinzufügen können, die die gereinigten Werte hält (das heißt alles, aber 0 und -1). Verwenden Sie diese Spalte für die referenziellen Integritätsprüfungen. Diese Schattenspalte wird durch einen einfachen Trigger auf table1 aktualisiert/gefüllt, der alle Werte außer 0 und -1 in die Schattenspalte schreibt. Sowohl 0 als auch -1 können auf null abgebildet werden.

Dann haben Sie Referenz Integrität und Ihre unveränderte ursprüngliche Spalte. Der Nachteil: Sie haben auch einen kleinen Trigger und einige redundante Daten. Aber leider ist dies das Schicksal eines Legacy-Schemas!

+0

Genie! Du hast Recht, dass es ein bisschen hässlich ist, aber die Datenbank verwaltet sich noch selbst, also sollte es den Trick tun! – trex005

+0

AH, hast du jemals an Postgre Source gearbeitet? Dies könnte eine coole Funktion sein, die Sie hinzufügen könnten, indem Sie genau dies hinter den Kulissen tun. – trex005

+1

@ trex005: Ich habe nicht an den PG-Quellen gearbeitet. Aber auf der Liste des Hackers hin und wieder habe ich den Eindruck, dass ein solches Feature nicht erwünscht wäre. Wenn Sie _ihr_ Beispiel verallgemeinern (mit irgendetwas außer '0' und '-1' übereinstimmen) und über eine generische Syntax nachdenken, die alle möglichen Fälle abdeckt, erhalten Sie ein ziemlich komplexes syntaktisches Konstrukt. Auf der anderen Seite der Skala: Mit den vorhandenen Werkzeugen kann man nichts machen. Das Endergebnis: Die knappen Ressourcen (Entwicklerzeit/Budget) werden sich bei anderen TODO-Items besser auszahlen. Aber das ist nur mein Eindruck. Fiel frei, sie zu kontaktieren. –

2

Die kurze Antwort ist nein, Postgres nicht bedingten Fremdschlüssel hat. Einige der möglichen Optionen sind:

  1. Nur keine FK-Einschränkung. Verschieben Sie diese Logik in die Datenzugriffsebene und leben Sie ohne die referenzielle Integrität.
  2. Erlauben Sie NULL in der Spalte, die auch mit einer FK-Einschränkung absolut gültig ist. Verwenden Sie dann eine andere Spalte, um die Bedeutung von 0 und -1 zu speichern.
  3. Fügen Sie eine Dummy-Zeile in die referenzierte Tabelle für 0 und -1 ein. Selbst wenn es nur falsche Daten hätte, würde es die FK-Beschränkung erfüllen.

Hoffe, das hilft!

+1

Das waren so ziemlich die Optionen, die ich erwartet hatte. Ich werde die Frage für eine Weile herumlassen in der Hoffnung, dass wir falsch liegen. :) – trex005

+2

Option 4 ändert immer den Postgres-Code, es * ist * Open Source Sie wissen :) –

+0

sehr sehr wahr ..... Was ist Ihr Gebot? : o) – trex005

1

Ihre Anforderung ist auf diese Check-Constraint-Äquivalent:

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

True, aber eine Prüfbedingung kann das Vorhandensein einer Zeile in einer anderen Tabelle nicht überprüfen. –

+0

@MikeChristensen Die Frage sagt die referenzierte Tabelle _hat alle positiven ganzen Zahlen (1 +) _. Ich weiß, dass das unmöglich ist, aber ich denke, er meint, dass es alle Zahlen ohne Lücken bis zu einer bestimmten Grenze hat. Dies würde mit der Hinzufügung einer anderen Bedingung zur Überprüfung abgedeckt werden (a <= n). –

+1

Möglich, ich nahm einfach an, der OP wollte eigentlich einen echten Fremdschlüssel. –

0

Hier ist eine andere Möglichkeit. Verwenden Sie die PG-Vererbung, um eine Partition der Tabelle zu erzwingen, die +1 in der Flag-Spalte hat und ansonsten. (Übliche Regeln/Auslöser, um dies zu erhalten.) Dann haben Sie die FK-Beziehung zwischen nur der untergeordneten Has_PLUS_ONE-Tabelle und der referenzierten Tabelle.

+0

Die Verwendung von PG-Vererbung ist fast nie die richtige Lösung für irgendetwas. Sie sind einfach zu begrenzt und skurril. – cliffordheath

0

Sie können dies mit einer Prüfbedingung und einem Fremdschlüssel implementieren.

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)); 

(nicht getestet)