Datenbankentwurf: Normalisieren einer (Viele-zu-Viele) -zu-Viele-Beziehung

13

Kurze Version

Ich muss jedem Paar in einem bestehenden Many-to-Many-Join eine feste Anzahl zusätzlicher Eigenschaften hinzufügen. Wenn Sie zu den folgenden Diagrammen übergehen, welche der Optionen 1 bis 4 ist in Bezug auf Vor- und Nachteile die beste Möglichkeit, dies durch Erweiterung des Basisgehäuses zu erreichen? Oder gibt es eine bessere Alternative, die ich hier nicht in Betracht gezogen habe?

Längere Version

Derzeit habe ich zwei Tabellen in einer Viele-zu-Viele-Beziehung über eine Zwischen-Verknüpfungstabelle. Ich muss jetzt zusätzliche Links zu Eigenschaften hinzufügen, die zu dem Paar vorhandener Objekte gehören. Ich habe eine feste Anzahl dieser Eigenschaften für jedes Paar, obwohl ein Eintrag in der Eigenschaftentabelle für mehrere Paare gelten kann (oder sogar mehrfach für ein Paar verwendet werden kann). Ich versuche, den besten Weg zu finden, um dies zu erreichen, und habe Probleme, die Situation einzuschätzen. Semantisch scheint es so, als ob ich es genauso gut wie eines der folgenden beschreiben kann:

  1. Ein Paar, das mit einem Satz einer festen Anzahl zusätzlicher Eigenschaften verknüpft ist
  2. Ein Paar verbunden mit vielen zusätzlichen Eigenschaften
  3. Viele (zwei) Objekte, die mit einer Gruppe von Eigenschaften verknüpft sind
  4. Viele Objekte, die mit vielen Eigenschaften verknüpft sind

Beispiel

Ich habe zwei Objekttypen, X und Y, jeweils mit eindeutigen IDs, und eine Verknüpfungstabelle objx_objymit Spalten x_idund y_id, die zusammen den Primärschlüssel für die Verknüpfung bilden. Jedes X kann mit vielen Ys verknüpft sein und umgekehrt. Dies ist das Setup für meine bestehende Many-to-Many-Beziehung.

Base Case

Grundkoffer

Zusätzlich habe ich eine Reihe von Eigenschaften in einer anderen Tabelle definiert und eine Reihe von Bedingungen, unter denen ein gegebenes (X, Y) Paar die Eigenschaft P haben soll. Die Anzahl der Bedingungen ist fest und für alle Paare gleich. Sie sagen im Grunde "In Situation C1 hat das Paar (X1, Y1) die Eigenschaft P1", "In Situation C2 hat das Paar (X1, Y1) die Eigenschaft P2" und so weiter für drei Situationen / Bedingungen für jedes Paar im Join Tabelle.

Option 1

In meiner aktuellen Situation gibt es genau drei solche Bedingungen, und ich habe keinen Grund zu erwarten, zu erhöhen , um eine Möglichkeit , Spalten hinzuzufügen ist c1_p_id, c2_p_idund c3_p_idzu der featx_featyAngabe , für ein gegebenen x_idund y_id, das Eigentum p_idan dem Einsatz in jedem der drei Fälle .

Option 1

Dies scheint mir keine gute Idee zu sein, da dies die Auswahl aller auf ein Feature angewendeten Eigenschaften in SQL erschwert und eine Skalierung auf weitere Bedingungen nicht ohne Weiteres möglich macht. Es wird jedoch die Anforderung einer bestimmten Anzahl von Bedingungen pro (X, Y) Paar durchgesetzt. Tatsächlich ist dies die einzige Option, die dies tut.

Option 2

Erstellen Sie eine Bedingungstabelle condund fügen Sie die Bedingungs-ID zum Primärschlüssel der Verknüpfungstabelle hinzu.

Option 2

Ein Nachteil davon ist, dass es nicht die Anzahl der Bedingungen für jedes Paar angibt. Eine andere ist, wenn ich nur über die anfängliche Beziehung zu etwas wie nachdenke

SELECT objx.*, objy.* FROM objx
  INNER JOIN objx_objy ON objx_objy.x_id = objx.id
  INNER JOIN objy ON objy.id = objx_objy.y_id

Ich muss dann eine DISTINCTKlausel hinzufügen , um doppelte Einträge zu vermeiden. Dies scheint die Tatsache verloren zu haben, dass jedes Paar nur einmal existieren sollte.

Option 3

Erstellen Sie eine neue 'Paar-ID' in der Verknüpfungstabelle und erstellen Sie eine zweite Verknüpfungstabelle zwischen der ersten und den Eigenschaften und Bedingungen.

Option 3

Dies scheint die geringsten Nachteile zu haben, abgesehen von dem Fehlen einer festgelegten Anzahl von Bedingungen für jedes Paar. Ist es sinnvoll, eine neue ID zu erstellen, die nichts anderes als vorhandene IDs identifiziert?

Option 4 (3b)

Grundsätzlich das Gleiche wie Option 3, jedoch ohne die Erstellung des zusätzlichen ID-Feldes. Dies wird erreicht, indem beide ursprünglichen IDs in die neue Verknüpfungstabelle eingefügt werden x_idund y_idstattdessen Felder und enthalten xy_id.

Option 4

Ein zusätzlicher Vorteil dieses Formulars besteht darin, dass die vorhandenen Tabellen nicht geändert werden (obwohl sie noch nicht in Produktion sind). Im Grunde genommen dupliziert es jedoch eine ganze Tabelle mehrmals (oder fühlt sich sowieso so an) und scheint daher auch nicht ideal zu sein.

Zusammenfassung

Mein Gefühl ist, dass die Optionen 3 und 4 so ähnlich sind, dass ich mit beiden Optionen fahren könnte. Ich hätte wahrscheinlich jetzt, wenn nicht für die Anforderung einer kleinen, festen Anzahl von Links zu Eigenschaften, die Option 1 vernünftiger erscheinen lässt, als es sonst wäre. Basierend auf einigen sehr eingeschränkten Tests DISTINCTscheint das Hinzufügen einer Klausel zu meinen Abfragen in dieser Situation keine Auswirkung auf die Leistung zu haben, aber ich bin nicht sicher, ob Option 2 die Situation und die anderen darstellt, da durch das Platzieren inhärente Duplikate auftreten Dieselben (X, Y) Paare in mehreren Zeilen der Verknüpfungstabelle.

Ist eine dieser Optionen meine beste Lösung, oder gibt es eine andere Struktur, die ich in Betracht ziehen sollte?

Michael Underwood
quelle
Insgesamt scheinen 1 und 4 die besten Optionen zu sein, da stimme ich zu. Es wäre nicht einfach, die feste (3) Anzahl von Eigenschaften mit Option 4 durchzusetzen, aber ich denke, dass es machbar ist.
Ypercubeᵀᴹ
Für die DISTINCTKlausel, wurde ich von einer Abfrage wie der am Ende von # 2 zu denken, die Links xund ydurch , xycsondern bezieht sich nicht c... Also , wenn ich (x_id, y_id, c_id)gezwungen UNIQUEmit Zeilen (1,1,1)und (1,1,2)dann SELECT x.id, y.id FROM x JOIN xyc JOIN ywerde ich wieder zwei identische bekommen Zeilen,, (1,1)und (1,1).
Michael Underwood
1
Ach ok Ich würde Option 2 sowieso ablehnen. Ich würde entweder mit 1 oder 4 gehen.
ypercubeᵀᴹ
Je mehr ich darüber nachdenke, desto weniger wichtig ist es für mich, die Anzahl der Objekte auf genau drei zu beschränken. Abgesehen von zusätzlichen konstruktiven Rückmeldungen in der nächsten Zeit werde ich an dieser Stelle wahrscheinlich mit # 4 weitermachen. Vielen Dank für Ihre Eingabe, @ypercube!
Michael Underwood

Antworten:

7
  • Option 1

    * Dies scheint mir keine großartige Idee zu sein, da es die SQL erschwert, alle Eigenschaften auszuwählen, die auf ein Feature angewendet werden.

    Abfrage-SQL wird dadurch nicht unbedingt kompliziert (siehe Schlussfolgerung unten).

    … Und lässt sich nicht ohne Weiteres auf weitere Bedingungen skalieren…

    Es lässt sich leicht auf mehr Bedingungen skalieren, solange es noch eine feste Anzahl von Bedingungen gibt und es nicht Dutzende oder Hunderte gibt.

    Es wird jedoch die Anforderung einer bestimmten Anzahl von Bedingungen pro (X, Y) Paar durchgesetzt. Dies ist in der Tat die einzige Option, die dies tut. *

    Das stimmt, und obwohl Sie in einem Kommentar sagen, dass dies "die unwichtigste meiner Anforderungen" ist, haben Sie nicht gesagt, dass es überhaupt keine Rolle spielt.

  • Option 2

    Ein Nachteil davon ist, dass es nicht die Anzahl der Bedingungen für jedes Paar angibt. Ein weiterer Grund ist, dass ich, wenn ich nur die ursprüngliche Beziehung betrachte, eine DISTINCT-Klausel hinzufügen muss, um doppelte Einträge zu vermeiden.

    Ich denke, Sie können diese Option aufgrund der von Ihnen erwähnten Komplikationen ablehnen. Die objx_objyTabelle ist wahrscheinlich die Ansteuertabelle für einige Ihrer Abfragen (z. B. "Alle auf ein Feature angewendeten Eigenschaften auswählen", womit alle auf ein objxoder angewendeten Eigenschaften gemeint sind objy). Sie können eine Ansicht verwenden, um die vorab anzuwenden, DISTINCTdamit es nicht zu komplizierten Abfragen kommt, aber dies wird sich in Bezug auf die Leistung sehr schlecht skalieren lassen und nur sehr wenig Gewinn bringen.

  • Option 3

    Ist es sinnvoll, eine neue ID zu erstellen, die nichts anderes als vorhandene IDs identifiziert?

    Nein, das ist nicht der Fall - Option 4 ist in jeder Hinsicht besser.

  • Option 4

    … Es dupliziert im Grunde genommen eine ganze Tabelle mehrmals (oder fühlt sich sowieso so an), scheint also auch nicht ideal zu sein.

    Diese Option ist in Ordnung - es ist die naheliegende Möglichkeit, die Beziehungen einzurichten, wenn die Anzahl der Eigenschaften variabel ist oder sich ändern kann

Fazit

Ich würde Option 1 bevorzugen, wenn die Anzahl der Eigenschaften pro objx_objywahrscheinlich stabil ist und Sie sich nicht vorstellen können, jemals mehr als eine Handvoll Extra hinzuzufügen. Es ist auch die einzige Option, die die Einschränkung 'Anzahl der Eigenschaften = 3' erzwingt - die Erzwingung einer ähnlichen Einschränkung für Option 4 würde wahrscheinlich das Hinzufügen von c1_p_id... Spalten zur xy-Tabelle beinhalten *.

Wenn Ihnen diese Bedingung wirklich nicht besonders wichtig ist und Sie auch Grund zu der Annahme haben, dass die Bedingung für die Anzahl der Eigenschaften stabil sein wird, wählen Sie Option 4.

Wenn Sie nicht sicher sind, welche, wählen Sie Option 1 - es ist einfacher und das ist definitiv besser, wenn Sie die Option haben, wie andere gesagt haben. Wenn Sie die Option 1 deaktiviert haben, "... weil dies die Auswahl aller auf ein Feature angewendeten Eigenschaften in SQL erschwert ...", empfehle ich, eine Ansicht zu erstellen, die dieselben Daten enthält wie die zusätzliche Tabelle in Option 4:

Option 1 Tische:

create table prop(id integer primary key);
create table objx(id integer primary key);
create table objy(id integer primary key);

create table objx_objy(
  x_id integer references objx
, y_id integer references objy
, c1_p_id integer not null references prop
, c2_p_id integer not null references prop
, c3_p_id integer not null references prop
, primary key (x_id, y_id)
);

insert into prop(id) select generate_series(90,99);
insert into objx(id) select generate_series(10,12);
insert into objy(id) select generate_series(20,22);

insert into objx_objy(x_id,y_id,c1_p_id,c2_p_id,c3_p_id)
select objx.id, objy.id, 90, 91, 90+floor(random()*10)
from objx cross join objy;

Ansicht, um Option 4 zu 'emulieren':

create view objx_objy_prop as
select x_id
     , y_id
     , unnest(array[1,2,3]) c_id
     , unnest(array[c1_p_id,c2_p_id,c3_p_id]) p_id
from objx_objy;

"Alle auf ein Feature angewendeten Eigenschaften auswählen":

select distinct p_id from objx_objy_prop where x_id=10 order by p_id;

/*
|p_id|
|---:|
|  90|
|  91|
|  97|
|  98|
*/

dbfiddle hier

Jack sagt, versuchen Sie es mit topanswers.xyz
quelle
-3

Ich glaube, dass jede dieser Optionen funktionieren könnte, aber ich würde Option 1 wählen, wenn die Anzahl der Bedingungen wirklich auf 3 festgelegt ist, und Option 2, wenn dies nicht der Fall ist. Das Rasiermesser von Occam eignet sich auch für das Datenbankdesign. Bei allen anderen Faktoren ist das einfachste Design in der Regel das beste.

Wenn Sie jedoch strenge Regeln für die Datenbanknormalisierung einhalten möchten, müssen Sie meines Erachtens mit 2 fortfahren, unabhängig davon, ob die Anzahl der Bedingungen festgelegt ist.

Matthew Sontum
quelle