it-swarm.com.de

Wie soll ich eine Beziehungstabelle für Freundschaft entwerfen?

Wenn A ein Freund von B ist, sollte ich dann beide Werte AB und BA speichern, oder einer ist genug? Was sind die Vor- und Nachteile beider Methoden?.

Hier ist meine Beobachtung:

  • Wenn ich beide behalte, muss ich beide aktualisieren, wenn ich eine Anfrage von einem Freund erhalte.
  • Wenn ich nicht beide behalte, fand ich es schwierig, mehrere JOIN mit dieser Tabelle zu machen.

Derzeit halte ich die Beziehung in eine Richtung.

enter image description here

Was soll ich in diesem Fall tun? Irgendein Rat?

34
Chan

Ich würde AB und BA speichern. Eine Freundschaft ist wirklich eine wechselseitige Beziehung, jede Entität ist mit einer anderen verbunden. Obwohl wir die "Freundschaft" intuitiv als eine Verbindung zwischen zwei Menschen betrachten, ist sie aus relationaler Sicht eher wie "A hat einen Freund B" und "B hat einen Freund A". Zwei Beziehungen, zwei Datensätze.

30
datagod

Wenn Freundschaft symmetrisch sein soll (dh es ist nicht möglich, dass A mit B befreundet ist, aber nicht umgekehrt), würde ich nur die Einwegbeziehung mit einer Prüfbeschränkung speichern, die sicherstellt dass jede Beziehung nur auf eine Weise dargestellt werden kann.

Außerdem würde ich die Ersatz-ID fallen lassen und stattdessen eine zusammengesetzte PK haben (und möglicherweise einen zusammengesetzten eindeutigen Index auch für die umgekehrten Spalten).

CREATE TABLE Friends
  (
     UserID1 INT NOT NULL REFERENCES Users(UserID),
     UserID2 INT NOT NULL REFERENCES Users(UserID),
     CONSTRAINT CheckOneWay CHECK (UserID1 < UserID2),
     CONSTRAINT PK_Friends_UserID1_UserID2 PRIMARY KEY (UserID1, UserID2),
     CONSTRAINT UQ_Friends_UserID2_UserID1 UNIQUE (UserID2, UserID1)
  ) 

Sie sagen nicht, dass dies schwierig ist, aber Sie können jederzeit eine Ansicht erstellen

CREATE VIEW Foo
AS
SELECT UserID1,UserID2 
FROM Friends
UNION ALL
SELECT UserID2,UserID1 
FROM Friends
13
Martin Smith

Angenommen, eine "Freundschaft" ist immer wechselseitig/gegenseitig, würde ich wahrscheinlich so etwas tun.

CREATE TABLE person (
    person_id int IDENTITY(1,1) PRIMARY KEY,
    ...other columns...
)

CREATE TABLE friendship (
    friendship_id int IDENTITY(1,1) PRIMARY KEY,
    ...other columns, if any...
)

CREATE TABLE person_friendship (
    person_id int NOT NULL,
    friendship_id int NOT NULL
    PRIMARY KEY (person_id, friendship_id)
)

Das Ergebnis ist, dass Sie es von einem Viele-zu-Viele-Beitritt von "Person" zu "Person" zu einem Viele-zu-Viele-Beitritt von "Person" zu "Freundschaft" ändern. Dies vereinfacht Verknüpfungen und Einschränkungen, hat jedoch den Nebeneffekt, dass mehr als zwei Personen in einer einzigen "Freundschaft" zugelassen werden (obwohl die zusätzliche Flexibilität möglicherweise ein potenzieller Vorteil wäre).

7
db2

Möglicherweise müssen Sie Indizes für Freundschaften definieren, anstatt die Anzahl der Zeilen zu verdoppeln:

CREATE TABLE person
(
    person_id INT NOT NULL AUTO_INCREMENT,
    ...
    PRIMARY KEY (person_id)
);
CREATE TABLE friendship
(
    friend_of INT NOT NULL,
    friend_to INT NOT NULL,
    PRIMARY KEY (friend_of,friend_to),
    UNIQUE KEY friend_to (friend_to,friend_of)
);

Auf diese Weise verdoppeln Sie den Speicher für Indizes, jedoch nicht für die Tabellendaten. Infolgedessen sollte dies eine Einsparung von 25% beim Speicherplatz bedeuten. Das MySQL Query Optimizer wählt nur die Durchführung von Indexbereichsscans aus, weshalb das Konzept der Abdeckung von Indizes hier gut funktioniert.

Hier sind einige nette Links zu Covering Indexes:

VORBEHALT

Wenn Freundschaft nicht gegenseitig ist, haben Sie die Grundlage für eine andere Art von Beziehung: FOLLOWER

Wenn friend_to kein Freund von friend_of ist, können Sie diese Beziehung einfach aus der Tabelle streichen.

Wenn Sie Beziehungen für alle Typen definieren möchten, unabhängig davon, ob sie gegenseitig sind oder nicht, können Sie wahrscheinlich das folgende Tabellenlayout verwenden:

CREATE TABLE person
(
    person_id INT NOT NULL AUTO_INCREMENT,
    ...
    PRIMARY KEY (person_id)
);
CREATE TABLE relationship
(
    rel_id INT NOT NULL AUTO_INCREMENT,
    person_id1 INT NOT NULL,
    person_id2 INT NOT NULL,
    reltype_id TINYINT,
    PRIMARY KEY (rel_id),
    UNIQUE KEY outer_affinity (reltype_id,person_id1,person_id2),
    UNIQUE KEY inner_affinity (reltype_id,person_id2,person_id1),
    KEY has_relationship_to (person1_id,reltype_id),
    KEY has_relationship_by (person2_id,reltype_id)
);
CREATE TABLE relation
(
    reltype_id TINYINT NOT NULL AUTO_INCREMENT,
    rel_name VARCHAR(20),
    PRIMARY KEY (reltype_id),
    UNIQUE KEY (rel_name)
);
INSERT INTO relation (relation_name) VALUES
('friend'),('follower'),('foe'),
('forgotabout'),('forsaken'),('fixed');

In der Beziehungstabelle können Sie die Beziehungen so anordnen, dass sie Folgendes enthalten:

  • Freunde sollten gegenseitig sein
  • Feinde können gegenseitig sein oder nicht
  • Anhänger könnten gegenseitig sein oder nicht
  • Die anderen Beziehungen würden interpretiert (vom Vergessenen oder Verlassenen oder vom Racheempfänger (fest)).
  • Mögliche Beziehungen können weiter ausgebaut werden

Dies sollte für alle Beziehungen robuster sein, unabhängig davon, ob die Beziehung gegenseitig ist oder nicht.

4
RolandoMySQLDBA

Wenn Sie innerhalb der Anwendung steuern können, dass die ID von A immer niedriger als die ID von B ist (bestellen Sie die IDs der A-, B-Elemente vorbestellen), können Sie die Frage ohne OR (wählen Sie, wo id_A =) stellen a AND id_B = b, anstatt zu fragen (id_A = a AND id_B = b) OR (id_A = b AND id_B = a)), und pflegen Sie auch die Hälfte der Datensätze, die Sie benötigen Dann sollten Sie ein anderes Feld verwenden, um den Status der Beziehung aufrechtzuerhalten (sind-Freunde, a-angefragt-zu-b, b-angefragt-zu-a, Ex-Freunde-a, Ex-Freunde-b) und Sie sind fertig.

Auf diese Weise habe ich mein Freundschaftssystem verwaltet. Dies vereinfacht das System und verwendet die Hälfte der Zeilen, die Sie für andere Systeme benötigen. Nur A ist gleich dem niedrigeren ID-Wert im Code.

1
appartisan