web-dev-qa-db-fra.com

Conception de la base de données: La meilleure structure de table pour capturer la relation utilisateur/ami?

J'essaie de concevoir un modèle de données qui indique qu'un utilisateur est l'ami d'un autre utilisateur. C’est ce que j’ai trouvé jusqu’à présent, mais cela semble maladroit, existe-t-il une meilleure solution?

User
=====
Id
Name
etc...

UserFriend
===========
UserId
FriendId
IsMutual
IsBlocked
30
Zachary Yates
UserRelationship
====
RelatingUserID
RelatedUserID
Type[friend, block, etc]

Convenez que la mutualité n'appartient pas à une colonne; rompt la normalisation.

52
chaos

Pour aller un enregistrement pour deux utilisateurs et éviter de consommer plus de mémoire que les méthodes proposées ne suggèrent (deux fois plus que nécessaire, car il existe deux enregistrements pour chaque utilisateur), vous pouvez procéder comme suit:

  1. Structure de la table:

    USER_RELATIONSHIP {
        user_first_id,
        user_second_id,
        type
    
        primary key(user_first_id, user_second_id)
    }
    
  2. Assurez-vous que: user_first_id < user_second_id

  3. La partie la plus intéressante - type: pour tous les états possibles d’une relation, vous créez les valeurs correspondantes. Par exemple:

    pending_first_second
    pending_second_first
    friends
    block_first_second
    block_second_first
    block_both
    

Ce que tu as:

  1. Le user_first_id < user_second_id garantit qu'il n'y a qu'un seul enregistrement D'une relation entre deux utilisateurs donnés, car cette contrainte et les contraintes de clé primaire Ne permettent pas de le placer autrement.
  2. En basculant correctement entre les états de relation, vous déterminez la relation entre chacun des deux utilisateurs. S'il n'y a pas de relation entre deux utilisateurs, il n'y a pas d'enregistrement.
  3. Pour connaître la relation entre deux utilisateurs (ainsi que la mise à jour), il vous suffit de lancer une seule requête:

    select * from USER_RELATIONSHIP where
        user_first_id = user1_id and
        user_second_id = user2_id;
    

    sans une instruction or qui vérifierait cette colonne vice-versa, ce qui est plus rapide.

Exemple de scénario :

  1. no record: ne sont pas en couple

  2. pending_first_second: le premier a fait une demande d'ami au second

  3. friends: le second a approuvé la demande d'ami

  4. no record: l'un des utilisateurs a retiré l'autre de ses amis

Cette solution est efficace en termes de mémoire et de vitesse, car vous créez, stockez et mettez à jour un seul enregistrement.

27
Artem Novikov

Je construis actuellement un site de réseautage social pour un client et j'ai exprimé les choses de cette façon

CREATE TABLE [dbo].[PersonFriend] (
    [Id]                          INT            IDENTITY (1, 1) NOT NULL,
    [Timestamp]                   DATETIME       NOT NULL,
    [ChangeUser]                  NVARCHAR (200) NOT NULL,
    [FriendStatusId]              TINYINT        NOT NULL,
    [Person1Id]                   INT            NOT NULL,
    [Person2Id]                   INT            NOT NULL,
    [Person1RequestTimestamp]     DATETIME       NOT NULL,
    [Person2AcknowledgeTimestamp] DATETIME       NULL
);

Chaque personne est stockée dans la table Personne (imaginez cela). Les champs Person1Id et Person2Id sont FK dans la table person. Je garde une liste de statut dans la table FriendStatus pour indiquer si une requête a été demandée, acceptée, refusée, ignorée, etc. Le champ Timestamp est standard dans ma conception pour indiquer la création d'un enregistrement (il s'agit d'un motif utilisé par la classe de persistance de base). ) et son type de dupliqué dans cette table, car Person1RequestTimestamp contient les mêmes données. Je capture également lorsque la personne 2 a vu la demande et a effectué une action (ce qui est indiqué dans FriendStatusId) et la stocke dans le Person2AcknowledgeTimestamp). 

L'une des hypothèses fondamentales de cette conception peut être affirmée que l'amitié demandée par Personne1 à Personne2 - si cette amitié est acceptée, l'amitié est considérée comme réciproque.

8
keithwarren7

Je ferais quelque chose de similaire à ce que vous avez, mais retirez le drapeau "IsMutual". Ajoutez simplement une seconde ligne avec des valeurs inverses quand c'est mutuel. Il ajoute des lignes, mais se sent beaucoup plus propre.

5
Jeffrey

Que penses-tu de ma solution?

Vous avez 3 tables

1 **the user** (id,.etc)
2 **friend_request**(id,flaggerID,flaggedID)
3 **friend**(id,frienderID,friendedID)

Vous vous connectez, vérifiez si je suis dans la table des amis, si oui, listez vos amis (je suis dans friender> liste friended) (je suis dans friended> listfriender)

Est-ce que j'ai une demande? (Suis-je en flaggedID?) Le connaissez-vous? Sinon, supprimez l'enregistrement. Si c'est le cas, créez un nouveau dossier à la demande lorsque je me mets dans le générateur de drapeaux et que le signaleur est activé. Nous avons maintenant une mutualité entre 2 enregistrements dans la table des requêtes, nous les supprimons donc et les plaçons dans la table des amis . Facile à séparer req/amis.

2
csigarena

Peut-être ajouter une table de relation, y placer les propriétés de relation et la référencer depuis UserFriend.

2
rjurney

Probablement au-dessus, mais on peut utiliser le Web sémantique pour modéliser cela. On peut utiliser le format FOAF (ami d'un ami). 

2
tuinstoel

Je l'ai fait comme ça:

Utilisateur TABLE

id  name
-----------
1   foo
2   roo
3   shoo
4   mooo

TABLE relation d'amitié

id   u_id f_id
--------------
1    1    2
2    2    1
3    3    1
4    1    3

Chaque fois qu'une demande d'ami est acceptée, insère la méthode inverse 1 2 & 2 1 et une requête simple:

$ufq=mysql_query("SELECT t1.f_id,t2.id,t2.name FROM friends AS t1, user AS t2 WHERE t1.u_id='$u_id' AND t2.id=t1.f_id ORDER BY t2.name ")or die(mysql_error());
while($fn=mysql_fetch_array($ufq)){
    echo $fn["name"].'<br>';
}
2
aaryadev

Avez-vous réellement besoin d’une table physique pour savoir s’il ya des amis communs? Pourquoi ne pas faire une requête SQL comme:

SELECT U.ID, U.NAME FROM USER U
INNER JOIN USERFRIEND UF
ON U.ID = UF.FRIENDID
WHERE U.ID = (SELECT USER.ID FROM USER
            WHERE USER.ID = friend_id AND USER.ID != your_id);

Les résultats de la requête doivent renvoyer tous les amis communs.

1
ChampionChicken

Les amitiés sont moins évidentes que les scénarios classiques d’employeur/patron et d’utilisateur/conjoint. L'amitié est-elle une relation ou une activité? J'ai reçu pas mal de critiques pour avoir négligé ces derniers. Quoi qu'il en soit, vous aurez probablement besoin de plusieurs tables, quel que soit le caractère générique de votre modèle de données.

1
acoustickitty

Eh bien, je suis en retard à la fête, mais voici ma candidature.

D'abord les tables:

User
-id

Type
-id

Relationship
-user_id_1
-user_id_2
-type_id

Maintenant, je veux garder ma table de type simple. Ainsi, les types que j’ajoute ne représentent que la relation d’un utilisateur avec un autre. Ils ne représentent jamais la relation bidirectionnelle. Par exemple:

friend
ignored

Cela facilite l'ajout de nouveaux types. Je n'ai pas à penser ou créer toutes les combinaisons possibles de tous mes types. Je viens d'ajouter le nouveau type.

Pour créer une amitié, vous avez besoin de 2 entrées dans la table de relations. Si les deux utilisateurs conviennent qu'ils sont amis, ils le sont également. Si un seul dit qu'il est ami avec l'autre et que l'autre le fait bloquer, ils ne sont pas amis.

Faire des requêtes est très simple. Voici comment obtenir tous les amis de MySQL:

SELECT u.* FROM user u
LEFT JOIN relationship r1 ON u.id = r1.user_id_2
LEFT JOIN relationship r2 ON u.id = r2.user_id_1
WHERE r1.user_id_1 = <my_user_id> # my relationship with them
AND r1.type_id = <friend_type_id> # i say i'm a friend
AND r2.user_id_2 = <my_user_id> # their relationship with me
AND r2.type_id = <friend_type_id> # they say they're friends

Je pense aussi que cette approche est plus "transaction sûre". Imaginez que vous envoyez une demande d'ami à quelqu'un et que vous bloquez ensuite cette personne. Si cette personne accepte plus tard la demande d'ami, cela n'a pas d'importance. Cela ne change pas l'état de la relation, car elle est complètement indépendante.

Si vous aviez plutôt un seul type représentant la relation bidirectionnelle, vous seriez obligé de faire une sorte d'évaluation dans votre code, de ce que devrait être exactement le nouvel état de la relation, une fois que l'ami a accepté la demande d'ami. Sinon, vous pourriez débloquer un utilisateur et faire de cette personne des amis avec l'utilisateur qu'il avait bloqué.

Je préférerais de loin gérer cela au niveau de la base de données. Si un groupe de programmeurs travaillent sur l'application, il ne faut pas longtemps avant que quelqu'un oublie ce problème quelque part et un bogue difficile à trouver est créé.

0
Bjørn Stenfeldt

D'après ce que j'ai compris, l'amitié est le résultat d'une relation entre deux utilisateurs (Utilisateur1 et Utilisateur2) et, du fait qu'un utilisateur1 peut avoir 0 utilisateur (s) ou plusieurs en tant qu'ami (s) et inversement utilisateur2, une table de jonction "Ami" Commes au milieu pour représenter cette relation comme ceci:

User1 id (int) nom d'utilisateur (chaîne)

User2 id (int) nom d'utilisateur (chaîne)

Ami ---- id (int) user1_Id (int) user2_Id (int) actif (bool)

User1_Id et user2_Id sont des FK dans la table Frind

J'espère que j'ai raison.

0
Xris

Je pense que vous devriez créer deux tables:

1. utilisateur
u_id int
u_nom_utilisateur chaîne
balahhh ............

2. relation amicale
fs_id int
mise en relation int
related_id int

0
bountyhunter