it-swarm.it

Come dovrei progettare una tabella delle relazioni per l'amicizia?

Se A è amico di B, allora dovrei memorizzare entrambi i valori AB e BA, o uno è sufficiente? Quali sono i vantaggi e gli svantaggi di entrambi i metodi.

Ecco la mia osservazione:

  • Se conservo entrambi, devo aggiornare entrambi quando ricevo una richiesta da un amico.
  • Se non li tengo entrambi, allora ho trovato difficile dover fare più JOIN con questa tabella.

Attualmente, mantengo la relazione in un modo.

enter image description here

Quindi cosa dovrei fare in questo caso? Qualche consiglio?

34
Chan

Conserverei AB e BA. Un'amicizia è davvero una relazione a doppio senso, ogni entità è collegata a un'altra. Anche se intuitivamente pensiamo all '"amicizia" come a un legame tra due persone, dal punto di vista relazionale è più simile a "A ha un amico B" e "B ha un amico A". Due relazioni, due record.

30
datagod

Se l'amicizia vuole essere simmetrica (cioè non è possibile che A sia amico di B ma non viceversa), memorizzerei la relazione unidirezionale con un vincolo di controllo assicurando che ogni relazione può essere rappresentata solo in un modo.

Inoltre vorrei abbandonare l'id surrogato e avere invece un PK composito (e possibilmente un indice univoco composito anche sulle colonne invertite).

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

Non dite le domande che ciò rende difficile, ma è sempre possibile creare una vista

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

Supponendo che una "amicizia" sia sempre bidirezionale/reciproca, probabilmente la gestirò in questo modo.

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

Il risultato è che lo si cambia da un'unione molti-a-molti da "persona" a "persona", a un'unione molti-a-molti da "persona" a "amicizia". Ciò semplifica le unioni e i vincoli, ma ha l'effetto collaterale di consentire a più di due persone in un'unica "amicizia" (anche se forse la flessibilità aggiuntiva sarebbe un potenziale vantaggio).

7
db2

Potrebbe essere necessario definire gli indici attorno alle amicizie invece di raddoppiare il numero di righe:

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

In questo modo, raddoppi la memoria per gli indici ma non per i dati della tabella. Di conseguenza, questo dovrebbe essere un risparmio del 25% sullo spazio su disco. MySQL Query Optimizer sceglierà di eseguire solo scansioni dell'intervallo di indici, motivo per cui il concetto di copertura degli indici funziona bene qui.

Ecco alcuni link utili sugli indici di copertura:

AVVERTIMENTO

Se l'amicizia non è reciproca, hai le basi per un altro tipo di relazione: FOLLOWER

Se friend_to non è amico di friend_of, puoi semplicemente lasciare quella relazione fuori dal tavolo.

Se si desidera definire relazioni per tutti i tipi, siano essi reciproci o meno, è possibile utilizzare il seguente layout di tabella:

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

Dalla tabella delle relazioni, è possibile organizzare le relazioni in modo da includere quanto segue:

  • Gli amici dovrebbero essere reciproci
  • I nemici potrebbero essere reciproci o no
  • I follower potrebbero essere reciproci o meno
  • Le altre relazioni sarebbero soggette a interpretazione (da parte dell'oblio o dell'oblio o dal destinatario della vendetta (fissa))
  • Le relazioni possibili possono essere ulteriormente estese

Questo dovrebbe essere più solido per tutte le relazioni, indipendentemente dal fatto che la relazione sia reciproca o meno.

4
RolandoMySQLDBA

Se è possibile controllare all'interno dell'applicazione che l'id di A sia sempre inferiore all'id di B (preordinare gli ID di elementi A, B) è possibile sfruttare la richiesta senza un OR (selezionare dove id_A = a AND id_B = b, invece di chiedere (id_A = a AND id_B = b) OR (id_A = b AND id_B = a)) e mantenere anche la metà dei record necessari con le approssimazioni dell'altro. Quindi dovresti usare un altro campo per mantenere lo stato della relazione (are-amici, a-sollecitati-a-b, b-sollecitati-a-a, ex-amici-a, ex-amici-b) e tu fatto.

Questo è il modo in cui ho gestito il mio sistema di amicizia, e questo semplifica il sistema e utilizza metà delle righe necessarie con altri sistemi, dicendo solo che A è uguale al valore ID inferiore nel codice.

1
appartisan