Komplexe Fremdschlüsseleinschränkung in SQLAlchemy

8

Ich habe zwei Tabellen, SystemVariables und VariableOptions . SystemVariables sollte selbsterklärend sein, und VariableOptions enthält alle möglichen Auswahlmöglichkeiten für alle Variablen.

VariableOptions hat einen Fremdschlüssel, variable_id , der angibt, für welche Variable es eine Option ist. SystemVariables hat einen Fremdschlüssel, choice_id , der angibt, welche Option die aktuell ausgewählte Option ist.

Ich habe die zirkuläre Beziehung mit use_alter in choice_id und post_update in SystemVariables ' choice Beziehung verstanden. Ich möchte jedoch eine zusätzliche Datenbankeinschränkung hinzufügen, die sicherstellt, dass choice_id gültig ist (d. H. Es bezieht sich auf eine Option, die darauf zurückführt).

Die Logik, die ich brauche, unter der Annahme, dass sysVar eine Zeile in der Tabelle SystemVariables darstellt, ist im Grunde:

%Vor%

Aber ich weiß nicht, wie ich diese Art von Constraint mit SQL, deklarativen oder anderen Methoden erstellen soll. Bei Bedarf könnte ich dies nur auf Anwendungsebene validieren, aber ich möchte es wenn möglich auf Datenbankebene haben. Ich benutze Postgres 9.1.

Ist das möglich?

    
Cam Jackson 06.12.2011, 01:20
quelle

3 Antworten

10

Sie können das ohne schmutzige Tricks implementieren. Erweitern Sie einfach den Fremdschlüssel , der auf die ausgewählte Option verweist, um variable_id zusätzlich zu choice_id einzufügen.

Hier ist eine funktionierende Demo. Temporäre Tabellen, damit Sie leicht damit spielen können:

%Vor%

Die Auswahl einer verknüpften Option ist erlaubt:

%Vor%

Aber es kommt nicht aus der Reihe:

%Vor%
%Vor%

Voilá . Genau das, was du wolltest.

Alle Schlüsselspalten sind NOT NULL

Ich denke, ich fand eine bessere Lösung in dieser späteren Antwort:

Adressierung der @ ypercubes Frage in den Kommentaren , Um Einträge mit unbekannter Verknüpfung zu vermeiden, machen Sie alle Schlüsselspalten NOT NULL , einschließlich Fremdschlüssel.

Die zirkuläre Abhängigkeit würde das normalerweise unmöglich machen. Es ist das klassische Chicken-Ei Problem: Eines von beiden muss zuerst da sein, um das andere zu spawnen. Aber die Natur hat einen Weg gefunden, und auch Postgres: aufschiebbare Fremdschlüsselbeschränkungen .

%Vor%

Neue Variablen und zugehörige Optionen müssen in dieselbe Transaktion eingefügt werden:

%Vor%

Die Einschränkung NOT NULL kann nicht zurückgestellt werden, sie wird sofort erzwungen. Aber die Fremdschlüsselbedingung kann , weil wir es so definiert haben. Es wird am Ende der Transaktion überprüft, wodurch das Hühnerei-Problem vermieden wird.

In diesem bearbeiteten -Szenario werden beide Fremdschlüssel zurückgestellt . Sie können Variablen und Optionen in beliebiger Reihenfolge eingeben.

Sie haben vielleicht bemerkt, dass die erste Fremdschlüsseleinschränkung keinen CASCADE -Modifikator hat. (Es wäre nicht sinnvoll, Änderungen an variableoptions.variable_id zu kaskadieren.

Auf der anderen Seite hat der zweite Fremdschlüssel einen CASCADE Modifikator und ist trotzdem als aufschiebbar definiert. Dies birgt einige Einschränkungen. Das Handbuch :

  

Andere referentielle Aktionen als die NO ACTION Prüfung können nicht zurückgestellt werden,   selbst wenn die Einschränkung als aufschiebbar deklariert ist.

NO ACTION ist der Standardwert.

Referenzielle Integritätsprüfungen für INSERT werden also zurückgestellt, die deklarierten kaskadierenden Aktionen für DELETE und UPDATE jedoch nicht. Das Folgende ist in PostgreSQL 9.0 oder 9.1 nicht zulässig, da Einschränkungen nach jeder Anweisung erzwungen werden:

%Vor%

Details:

Seltsamerweise funktioniert das Gleiche in PostgreSQL 8.4 , während in der Dokumentation dasselbe Verhalten behauptet wird. Sieht in der alten Version wie ein Bug aus - auch wenn es auf den ersten Blick eher vorteilhaft als schädlich erscheint. Muss für neuere Versionen behoben worden sein.

    
Erwin Brandstetter 06.12.2011, 03:32
quelle
4

BEARBEITEN: Die Version 0.7.4 von SQLAlchemy (veröffentlicht am selben Tag, an dem ich nach diesem Problem gefragt habe, 7/12 / '11!) enthält einen neuen autoincrement -Wert für Primärschlüssel das ist auch ein Teil der Fremdschlüssel, ignore_fk . Die Dokumentation wurde ebenfalls erweitert, um ein gutes Beispiel für das zu geben, was ich ursprünglich erreichen wollte.

Alles ist jetzt gut erklärt here .

Wenn Sie den Code sehen möchten, den ich vor der obigen Version entwickelt habe, überprüfen Sie den Überarbeitungsverlauf dieser Antwort.

    
Cam Jackson 06.12.2011 23:59
quelle
2

Ich mag Zirkuläre Referenzen wirklich nicht. Es gibt normalerweise einen Weg, sie zu vermeiden. Hier ist ein Ansatz:

%Vor%     
ypercubeᵀᴹ 06.12.2011 22:22
quelle