2008-10-01 10 views
6

Je travaille sur une base de données qui doit représenter les ordinateurs et leurs utilisateurs. Chaque ordinateur peut avoir plusieurs utilisateurs et chaque utilisateur peut être associé à plusieurs ordinateurs. Il s'agit donc d'une relation classique plusieurs-à-plusieurs. Cependant, il doit également y avoir un concept d'utilisateur "principal". Je dois pouvoir me joindre à l'utilisateur principal pour lister tous les ordinateurs avec leurs utilisateurs principaux. Je ne suis pas sûr de la meilleure façon de structurer cela dans la base de données:Plusieurs-à-plusieurs avec "primaire"

1) Comme je le fais actuellement: table de liaison avec une colonne booléenne IsPrimary. L'assemblage nécessite quelque chose comme ON (c.computer_id = l.computer_id ET l.is_primary = 1). Cela fonctionne, mais cela ne va pas, car il n'est pas facile de limiter les données à un seul utilisateur principal par ordinateur.

2) Un champ sur la table de l'ordinateur qui pointe directement sur une ligne utilisateur, toutes les lignes de la table utilisateur représentent des utilisateurs non principaux. Cela représente mieux la contrainte "un primaire par ordinateur", mais rend plus difficile l'obtention d'une liste d'utilisateurs d'ordinateurs.

3) Un champ sur la table de l'ordinateur reliant à une ligne dans la table de liaison. Se sent étrange ...

4) Quelque chose d'autre?

Quelle est la manière «relationnelle» de décrire cette relation?

EDIT: @Mark Brackett: La troisième option me semble beaucoup moins étrange maintenant que vous avez montré à quel point elle peut être belle. Pour une raison quelconque, je n'ai même pas pensé à utiliser une clé étrangère composée, alors je pensais que je devrais ajouter une colonne d'identité sur la table de liaison pour que ça fonctionne. Ça a l'air génial, merci!

@ j04t: Cool, je suis content que nous soyons d'accord sur # 3 maintenant.

+0

pourquoi il est difficile d'énumérer l'utilisateur de l'ordinateur sur le point 2? – albertein

+0

Parce que la table de liaison ne contient pas tous les utilisateurs pour un ordinateur donné. –

Répondre

7

L'option 3, bien qu'elle puisse paraître étrange, est la plus proche de ce que vous voulez modéliser. Tu ferais quelque chose comme:

User { 
    UserId 
    PRIMARY KEY (UserId) 
} 

Computer { 
    ComputerId, PrimaryUserId 
    PRIMARY KEY (UserId) 
    FOREIGN KEY (ComputerId, PrimaryUserId) 
     REFERENCES Computer_User (ComputerId, UserId) 
} 

Computer_User { 
    ComputerId, UserId 
    PRIMARY KEY (ComputerId, UserId) 
    FOREIGN KEY (ComputerId) 
     REFERENCES Computer (ComputerId) 
    FOREIGN KEY (UserId) 
     REFERENCES User (UserId) 
} 

Ce qui vous donne 0 ou 1 utilisateur principal (le PrimaryUserId peut être annulable si vous voulez), qui doit être Computer_User. Éditer: Si un utilisateur ne peut être que primaire pour 1 ordinateur, alors une CONSTAT UNIQUE sur Computer.PrimaryUserId l'appliquera.Notez qu'il n'y a aucune exigence que tous les utilisateurs soient un primaire sur certains ordinateurs (ce serait une relation 1: 1, et demanderait qu'ils soient dans la même table).

Edit: Certaines requêtes pour vous montrer la simplicité de cette conception

--All users of a computer 
SELECT User.* 
FROM User 
JOIN Computer_User ON 
    User.UserId = Computer_User.UserId 
WHERE 
    Computer_User.ComputerId = @computerId 

--Primary user of a computer 
SELECT User.* 
FROM User 
JOIN Computer ON 
    User.UserId = Computer.PrimaryUserId 
WHERE 
    Computer.ComputerId = @computerId 

--All computers a user has access to 
SELECT Computer.* 
FROM Computer 
JOIN Computer_User ON 
    Computer.ComputerId = Computer_User.ComputerId 
WHERE 
    Computer_User.UserId = @userId 

--Primary computer for a user 
SELECT Computer.* 
FROM Computer 
WHERE 
    PrimaryUserId = @userId 
+0

L'ordinateur FK -> CompUser fait en sorte que l'utilisateur principal d'un ordinateur est bien l'un des utilisateurs de cet ordinateur. La rigueur est bonne :) – helios

+0

Merci pour la publication. Cela m'a permis de regarder la relation d'une manière qui semble initialement étrangère, mais qui fonctionne vraiment bien. –

+0

Je l'ai aimé! D'un point de vue théorique, nous disons ici que nous avons une relation "un-à-plusieurs" appelée "est l'utilisateur principal de l'ordinateur ..." en plus de la relation "plusieurs-à-plusieurs" appelée "est un utilisateur d'ordinateur ". Est-ce que quelqu'un a vu une limitation à cette proposition? –

0

Étant donné que l'utilisateur principal est une fonction de l'ordinateur et l'utilisateur, je voudrais aller avec votre approche d'avoir l'utilisateur primaire étant une colonne sur la table de liaison.

L'autre alternative que je peux penser est d'avoir une colonne primaryUser directement sur la table de l'ordinateur elle-même.

0

J'aurais fait une autre table PRIMARY_USERS avec unique sur computer_id et faisant à la fois computer_id et user_id clés étrangères des UTILISATEURS.

0

La solution 1 ou 2 fonctionnera. À ce stade, je me demande quel sera le plus facile de travailler avec. J'ai utilisé les deux méthodes dans des situations différentes, mais j'allais généralement avec un indicateur sur la table de liaison, puis je forçais une contrainte unique sur computer_id et isPrimaryUser, de telle sorte que chaque ordinateur ne possède qu'un seul utilisateur principal.

+0

à l'exception de la faille évidente des utilisateurs non primaires qui ont dupliqué ID_ordinateur, valeurs isPrimaryUser .... –

+0

Il suffit d'implémenter une seconde clé unique pour que user_id et isPrimaryUser soient également uniques. Garde tout à la maison dans une table. –

+0

IsPrimaryUser peut être 0 ou 1. Par conséquent, votre clé unique (UserId, IsPrimary) aurait un utilisateur * au plus * 1 utilisateur non primaire (UserId, IsPrimary = 0) et 1 utilisateur principal (UserId, IsPrimary = 1) . Votre (ComputerId, IsPrimary) a une faille similaire. –

0

2 me semble juste, mais je testerais 1, 2 et 3 pour les performances sur les sortes de requêtes que vous effectuez normalement et les sortes de volumes de données que vous avez. En règle générale, j'ai tendance à croire que lorsque vous avez le choix entre plusieurs implémentations, vous devez vous baser sur les exigences de vos requêtes et concevoir votre schéma pour obtenir les meilleures performances et l'utilisation des ressources dans le cas le plus courant.

Dans la rare situation où vous avez des cas tout à fait communs qui suggèrent des implémentations opposées, alors utilisez le rasoir d'Occam.

+0

Je ne suis pas d'accord que vous avez beaucoup de choix quand il s'agit de la modélisation relationnelle. Il y a * un * bon chemin, et un mauvais chemin. Le mauvais chemin peut mieux fonctionner - mais ce sera un problème de maintenance. Parfois, nous devons faire ce compromis, mais ce devrait être un choix conscient. –

+0

Juste à suivre - l'option 2 est * fausse * parce qu'elle permettrait à un utilisateur d'être à la fois un utilisateur principal (en étant dans Computer.PrimaryUserId) et un utilisateur non primaire (en étant dans Computer_User) pour le même ordinateur au même temps. –

1

Éditer - Je ne pensais pas correctement à ce sujet les 3 premières fois à travers ... je vote - (numéro 3 solution)

utilisateurs

user id (pk) 

Ordinateurs

computer id (pk) 
primary user id (fk -> computer users id) 

utilisateurs d'ordinateurs

user id (pk) (fk -> user id) 
computer id (pk) (fk -> user id) 

C'est la meilleure solution que je peux penser . Pourquoi j'aime cette conception.

1) Comme il s'agit d'une relation impliquant des ordinateurs et des utilisateurs, j'aime l'idée de pouvoir associer un utilisateur à plusieurs ordinateurs en tant qu'utilisateur principal. Cela peut ne jamais se produire lorsque cette base de données est utilisée.

2) La raison pour laquelle je n'aime pas avoir la primary_user sur la table de lien

(computer_users.primary_user_id fk-> users.user_id) 

est d'empêcher un ordinateur de jamais avoir plusieurs utilisateurs primaires. Compte tenu de ces raisons, la solution numéro 3 semble mieux puisque vous ne rencontrerez jamais de problèmes possibles que je vois avec les autres approches.

Solution 1 problème - Possibilité d'avoir plusieurs utilisateurs principaux par ordinateur.

Problème de la solution 2 - Liens de l'ordinateur vers un utilisateur principal lorsque l'ordinateur et l'utilisateur ne sont pas liés les uns aux autres.

computer.primaryUser = user.user_id 
computer_users.user_id != user.user_id 

Solution 3 problème - Cela semble-t-il plutôt étrange? À part ça, je ne peux penser à rien.

Solution 4 problème - Je ne peux penser à aucune autre manière de le faire.


Ceci est la quatrième édition, donc j'espère que cela a encore du sens.

+0

Cela ne résout toujours pas le problème de la détermination d'un utilisateur "principal". – Mark

0

Nous avons une situation similaire dans l'application où je travaille où nous avons des comptes qui peuvent avoir de nombreux clients attachés, mais un seul devrait être le client principal.

Nous utilisons une table de liens (comme vous l'avez) mais vous avez une valeur de séquence sur la table de liens. L'utilisateur primaire est celui avec la séquence = 1. Ensuite, nous avons un index sur cette table de liaison pour AccountID et la séquence pour s'assurer que la combinaison de AccountID et de séquence est unique (assurant ainsi que deux clients ne peuvent pas être le principal sur un compte). Donc, vous auriez:

LEFT JOIN c.computer_id = l.computer_id AND l.sequence = 1 
Questions connexes