2010-05-21 5 views
6

Imaginez que vous vivez dans un terrain d'exemple très simplifié - et imaginez que vous avez une table de personnes dans votre base de données MySQL:Vous avez un tableau de personnes, que je veux relier les unes aux autres, plusieurs-à-plusieurs, avec les liens bidirectionnels

create table person (
    person_id int, 
    name text 
) 

select * from person; 

+-------------------------------+ 
| person_id |   name | 
+-------------------------------+ 
|   1 |   Alice | 
|   2 |    Bob | 
|   3 |   Carol | 
+-------------------------------+ 

et ces gens ont besoin de collaborer/travailler ensemble, de sorte que vous avez une table de lien qui relie un enregistrement de personne à l'autre:

create table person__person (
    person__person_id int, 
    person_id int, 
    other_person_id int 
) 

Cela signifie configuration que les liens entre les gens sont unidirectionnels - c.-à-d. Alice peut lier à Bob, sans que Bob ne soit lié à Alice et même orse, Alice peut créer un lien vers Bob et Bob peut créer un lien vers Alice en même temps, dans deux enregistrements de liens distincts. Comme ces liens représentent des relations de travail, dans le monde réel, ils sont tous des relations mutuelles à double sens. Les éléments suivants sont possibles dans cette configuration:

select * from person__person; 

+---------------------+-----------+--------------------+ 
| person__person_id | person_id | other_person_id | 
+---------------------+-----------+--------------------+ 
|     1 |   1 |     2 | 
|     2 |   2 |     1 | 
|     3 |   2 |     2 | 
|     4 |   3 |     1 | 
+---------------------+-----------+--------------------+ 

Par exemple, avec person__person_id = 4 ci-dessus, lorsque vous affichez le profil de Carol (person_id = 3), vous devriez voir une relation avec Alice (person_id = 1) et quand Si vous regardez le profil d'Alice, vous devriez voir une relation avec Carol, même si le lien va dans l'autre sens.

Je me rends compte que je peux faire des requêtes syndicales et distinctes et que dire des relations mutuelles dans l'interface utilisateur, mais y a-t-il un meilleur moyen? J'ai le sentiment que est un meilleur moyen, celui où ce problème se fondrait en installant correctement la base de données, mais je ne peux pas le voir. Quelqu'un a une meilleure idée?

+3

C'est la question la plus formatée que j'ai jamais vu d'un utilisateur de moins de 50 rep! À votre santé! –

+1

J'ai changé les balises de 'mysql many-to-many' à 'data-modeling many-to-many' (+1 bonne question!) – lexu

+1

+1 question bien posée. nit-pick: person__person est un horrible nom de table; Réclame que c'est pour des fins d'illumination;) – msw

Répondre

3

Je ne sais pas s'il y a une meilleure façon de configurer vos tables. Je pense que la façon dont vous les avez est correcte et serait la façon dont je l'appliquerais.

Puisque votre table de relation peut indiquer des relations unidirectionnelles, je suggère de les traiter comme tel. En d'autres termes, pour chaque relation, j'ajouterais deux lignes. Si Alice collabore avec Bob, le tableau devrait être comme suit:

select * from person__person; 
+---------------------+-----------+--------------------+ 
| person__person_id | person_id | other_person_id | 
+---------------------+-----------+--------------------+ 
|     1 |   1 |     2 | 
|     2 |   2 |     1 | 
+---------------------+-----------+--------------------+ 

La raison est parce que dans beaucoup de ActiveRecord (Rails) comme les systèmes, les nombreux à plusieurs objets de table ne serait pas assez intelligent interroger à la fois person_id et other_person_id. En conservant deux lignes, les objets similaires à ActiveRecord fonctionneront correctement. Ce que vous devez faire est alors de renforcer l'intégrité de vos données au niveau du code.

Chaque fois qu'une relation est établie entre deux utilisateurs, deux enregistrements doivent être insérés. Lorsqu'une relation est détruite, les deux enregistrements doivent être supprimés. Les utilisateurs ne devraient pas être autorisés à établir des relations avec eux-mêmes.

+1

Cela a également le mérite de la logique métier de décider que toutes les associations ne sont pas bidirectionnelles et qu'aucune modification n'est nécessaire pour le schéma. – msw

+0

Fait et fait! Merci beaucoup - je vais avec cette méthode, pour toutes les raisons à la fois user239098 et msw déclaré. –

2

Il n'y a pas de meilleure idée. Les bases de données relationnelles ne peuvent pas appliquer ce que vous demandez, donc vous devez écrire une requête spéciale pour récupérer les données et un déclencheur pour appliquer la contrainte.

Pour obtenir les personnes liées à @person je pencherais pour:

SELECT CASE person_id WHEN @person 
      THEN other_person_id 
      ELSE person_id 
     END as related_person_id 
FROM person_person 
WHERE ( [email protected] 
     OR other_pers[email protected]) 
+0

Toujours mettre() autour d'un 'ou' dans la clause where. De mauvaises choses arriveront quand vous oubliez avant d'ajouter plus de contraintes! (+1 pour la réponse) – lexu

2

Je ne vois pas comment utiliser des concepts relationnels simples. Vous devrez ajouter un code «métier» pour renforcer vos relations personne-personne.

  • Une façon peut-être de faire appliquer un second enregistrement de relation à l'aide d'un insert déclencheur
  • puis déclarer l'un des enregistrements « primaire » (par exemple, celui où l'person_id est plus petit que l'autre _person_id)
  • puis Créez des vues et collez du code pour vos applications (sélectionner, mettre à jour, supprimer) qui accèdent aux données avec cette connaissance.
2

Vous devriez trouver ce poste utile:

http://discuss.joelonsoftware.com/default.asp?design.4.361252.31

Comme @user affiché vous êtes généralement mieux créer deux enregistrements par relation bidirectionnelle (Alice à Bob et Bob à Alice dans votre exemple). Cela rend beaucoup plus facile l'interrogation et reflète fidèlement les relations. Si vous avez de vraies relations unidirectionnelles, c'est la seule façon de voler.

+0

Merci, j'ai lu ce fil; il a essentiellement confirmé la solution «juste stocker deux fois», suivie par une charge de cris. Je suis content que stackoverflow ne soit pas comme ça :) –