So implementieren Sie die Eindeutigkeit, wobei die Reihenfolge der Felder keine Rolle spielt

8

Ich denke, das folgende Beispiel wird die Situation am besten erklären. Sagen wir, wir haben die folgende Tabellenstruktur:

%Vor%

Hier sind die Tabelleninhalte für die Tabelle:

%Vor%

Meine Frage ist, wie kann ich die Eindeutigkeit implementieren, so dass die folgende INSERT-Anweisung FAIL basierend auf dieser einen bereits in der Tabelle vorhandenen Zeile

ist %Vor%

Im Grunde versuche ich, eine Beziehung zwischen zwei Mitgliedern zu modellieren. Das Feld Status ist dasselbe, unabhängig davon, ob wir (100,105) oder (105,100) haben.

Ich weiß, ich könnte einen before_insert- und before_update-Trigger verwenden, um den Inhalt der Tabelle zu überprüfen. Aber ich habe mich gefragt, ob es einen besseren Weg dafür gibt ... Sollte mein Datenbankmodell anders sein ...

    
Cristina 14.03.2012, 16:56
quelle

8 Antworten

5

Wenn Sie sicherstellen können, dass alle Anwendungen / Benutzer die IDs der Mitglieder in der Reihenfolge der geringsten bis zur höchsten Ordnung speichern (die kleinste Mitglied-ID in Member1 und die größte in Member2 ), können Sie einfach eine Check-Einschränkung hinzufügen :

%Vor%

Wenn Sie das nicht möchten oder möchten, dass diese zusätzlichen Informationen gespeichert werden (weil Sie nur ein Beispiel speichern), dass "Mitglied 100 eingeladen (gemocht, getötet, was auch immer) Mitglied 150" und nicht umgekehrt), dann könnten Sie @ Tegiris Ansatz verwenden, der ein wenig modifiziert wurde (die Multiplikation von zwei ausreichend großen ganzen Zahlen wäre sonst ein Überlaufproblem):

%Vor%     
ypercubeᵀᴹ 14.03.2012, 18:04
quelle
3

Das Datenbankmodell schlägt fehl, weil Sie zwei Entitäten haben (Member1, Member2). Wenn Sie sagen, dass es egal ist, was Sie sagen, sind Sie dieselbe Entität {Member}. Mit anderen Worten, Sie haben eine Tatsache an zwei Stellen, eine der Hauptsünden des relationalen Datenbankentwurfs.

Eine Lösung auf hoher Ebene wäre, die Art der Beziehung besser zu modellieren. Ein Beispiel könnte eine Ehe zweier Personen sein. Anstatt "Braut und Bräutigam sind verheiratet" und fusseln über die zuerst aufgeführt wird, hätten Sie "Ehe #xyz ist zwischen (enthält) Teilnehmer A und B". Also, Tabelle Ehe mit einem Primärschlüssel, Tabelle MarriageMember mit Fremdschlüssel zur Eheschließung, Fremdschlüssel für "Person" und einen Primärschlüssel für beide Spalten. Lässt du mehr als zwei Mitglieder haben, was nützlich sein kann, wenn du in einer Heinlein-Geschichte bist.

Wenn Sie mit vorhandenen Schemas (und nicht wir alle) festgefahren sind, würde ich verlangen, dass die Daten mit, sagen wir, dem niedrigsten Wert zuerst aufgeführt werden, damit sie immer richtig geordnet sind. Sie könnten Tricks mit einer Prüfsumme für die beiden Spalten als berechnete Spalte machen, aber das würde die Eindeutigkeit nicht absolut garantieren. Aber leider, am Ende des Tages scheint Ihr Modell für Ihre Zwecke leicht fehlerhaft.

Nachtrag

Wenn Sie gemäß den folgenden Kommentaren Mitglieder modellieren, denen ein bestimmtes Mitglied angehört, haben Sie eine Situation "Mitglied ist verwandt mit anderen Mitgliedern". Hier ist Member1 das "Hauptmitglied" und Member2 ist ein anderes Mitglied, mit dem "dieses" Mitglied verwandt ist. (Und das ist die Unterscheidung, die zwischen den zwei Mitgliedsspalten benötigt wird.) Wenn also eine Beziehung bidirektional ist, dann würden Sie zwei Einträge benötigen, um sowohl "Mitglied A ist mit Mitglied B" und "Mitglied B ist mit Mitglied A verwandt". Dies würde natürlich mit einem Primärschlüssel auf {Member1, Member2} erzwungen werden, da Status scheinbar irrelevant ist (es gibt nur eine Beziehung, nicht mehrere basierend auf dem Status).

    
Philip Kelley 14.03.2012 17:21
quelle
2

Eine Möglichkeit, einen Trigger zu vermeiden, ist eine UNIQUE-berechnete Spalte in Member1 und Member2:

%Vor%     
John Dewey 14.03.2012 17:23
quelle
2

Hier ist eine alternative Betrachtungsweise. Sie könnten tatsächlich die Regel erzwingen, dass die gegenseitige Beziehung immer durch das Vorhandensein von zwei Zeilen ausgedrückt wird, (A, B) und (B, A) statt nur einer.

%Vor%     
sqlvogel 14.03.2012 22:07
quelle
1

Ich kann mir keinen besseren Weg vorstellen, um die bestehende einzigartige Einschränkung neben einem Trigger zu erweitern. z.B.

%Vor%

Jetzt geht es mir nur noch um einreihige Inserts. Die Logik kann etwas komplizierter werden, wenn Sie mit mehrzeiligen Einfügungen arbeiten müssen, da Sie sowohl innerhalb von inserted als auch zwischen inserted und der Basistabelle nach Duplikaten suchen müssen. Dies bewältigt auch keine hohe Parallelität bei der Standardisolationsstufe (z.B. fügt eine andere Transaktion eine doppelte Zeile zwischen der Prüfung und der Einfügung ein). Aber das sollte ein Anfang sein.

(Sie benötigen auch eine für UPDATE ...)

    
Aaron Bertrand 14.03.2012 17:21
quelle
1

Hier finden Sie einen Auszug aus "Symmetrische Funktionen" im Buch "SQL Design patterns", die Sie möglicherweise finden.

Betrachten Sie eine Inventardatenbank von Boxen

%Vor%

Box Dimensionen in der realen Welt sind jedoch in der Regel nicht in einer bestimmten Reihenfolge angegeben. Die Auswahl, welche Dimensionen zu Länge, Breite und Höhe werden, ist im Wesentlichen beliebig. Was, wenn wir die Boxen anhand ihrer Abmessungen identifizieren möchten? Wir möchten zum Beispiel sagen können, dass die Box mit Länge = 1, Breite = 2 und Höhe = 3 dieselbe Box ist wie die mit Länge = 3, Breite = 1 und Höhe = 2. Wie wäre es mit einer eindeutigen Dimensionsbeschränkung? Genauer gesagt, wir erlauben keine zwei Boxen, die die gleichen Abmessungen haben.

Ein analytischer Verstand würde problemlos erkennen, dass das Kernstück des Problems die Reihenfolge der Spalten ist. Die Werte der Spalten Länge, Breite und Höhe können vertauscht werden, um einen anderen legitimen Datensatz zu bilden! Warum also führen wir nicht 3 Pseudospalten ein, sagen wir A, B und C, so dass

%Vor%

Dann sollte eine eindeutige Einschränkung für A, B, C unsere Anforderung erfüllen! Es könnte als ein funktionsbasierter eindeutiger Index implementiert werden, solange wir A, B, C analytisch in Bezug auf Länge, Breite und Höhe ausdrücken können. Stück Kuchen: A ist der größte von Länge, Breite, Höhe; C ist der kleinste von ihnen, aber wie drücken wir B aus? Nun, die Antwort ist einfach zu schreiben

%Vor%

obwohl schwer zu erklären.

Eine mathematische Perspektive verdeutlicht wie immer viel. Betrachten Sie die kubische Gleichung

Wenn wir die Wurzeln x1, x2, x3 kennen, dann könnte das kubische Polynom faktorisiert werden, so dass wir

haben

Wenn wir beide Gleichungen zusammenbringen, drücken wir die Koeffizienten a, b, c in Bezug auf die Wurzeln x1, x2, x3

aus

Abbildung 4.1: Eine Form des Graphen des Polynoms y=(x-x1)(x-x2)(x-x3) wird vollständig durch die Wurzeln x1, x2 und x3 definiert. Der Austausch hat keinen Einfluss auf irgendetwas.

Die Funktionen -x1-x2-x3, x1x2+x2x3+x3x1, -x1x2x3 sind symmetrisch. Das Setzen von x1, x2, x3 hat keinen Einfluss auf die Werte a, b, c. Mit anderen Worten, die Reihenfolge unter den Wurzeln der kubischen Gleichung ist irrelevant: formal sprechen wir von einer Menge von Wurzeln, nicht von einer Liste von Wurzeln1. Dies ist genau der Effekt, den wir in unserem Beispiel mit Boxen wollen. Symmetrische Funktionen, die in Länge, Breite und Höhe umgeschrieben sind, sind

%Vor%

Diese Ausdrücke wurden ein wenig vereinfacht, indem man die Tatsache nutzte, dass die Negation einer symmetrischen Funktion ebenfalls symmetrisch ist.

Unsere letzte Lösung ist auffallend ähnlich der früheren, wo der größte Operator die Rolle der Multiplikation spielt, während der kleinste Operator als Addition gilt. Es ist sogar möglich, eine Lösung vorzuschlagen, die ein Mix zwischen den beiden ist %Vor%

Ein Leser kann überprüfen, dass diese drei Funktionen wieder symmetrisch sind2. Der letzte Schritt ist die Aufzeichnung unserer Lösung in formellem SQL

%Vor%

Symmetrische Funktionen bieten die Grundlage für eine raffinierte Lösung. In der Praxis kann ein Problem jedoch oft durch ein Schema-Redesign gelöst werden. Im Beispiel der Box-Inventardatenbank brauchen wir nicht einmal ein Schema-Redesign: Wir können lediglich verlangen, die Praxis des Einfügens unbeschränkter Datensätze (length,width,height) zu ändern und fordern das

%Vor%     
Tegiri Nenashi 14.03.2012 17:24
quelle
1

Eine geringfügige Änderung der @ ypercube-Lösung wäre, eine indizierte Sicht zu erstellen und die eindeutige Einschränkung in die Sicht zu verschieben. Hier ist ein vollständiges Skript, das den Ansatz demonstriert:

%Vor%

Lesen Sie mehr über indizierte Sichten auf MSDN:

Andriy M 15.03.2012 06:42
quelle
0

Anstatt zu versuchen, die Tabelle selbst dazu zu bringen, diese spezielle Geschäftslogik zu erzwingen, wäre es besser, sie in einer gespeicherten Prozedur einzubetten? Sie gewinnen sicherlich mehr Flexibilität bei der Durchsetzung einzigartiger Beziehungen zwischen zwei Mitgliedern.

    
Thomas Bates 14.03.2012 18:34
quelle