2008-10-01 11 views
6

Estoy trabajando en una base de datos que necesita representar computadoras y sus usuarios. Cada computadora puede tener múltiples usuarios y cada usuario puede asociarse con varias computadoras, por lo que es una relación clásica de muchos a muchos. Sin embargo, también debe existir un concepto de usuario "principal". Tengo que ser capaz de unirme contra el usuario principal para listar todas las computadoras con sus usuarios principales. No estoy seguro de cuál es la mejor manera de estructurar esto en la base de datos:Many-to-Many con "Primary"

1) Como lo estoy haciendo actualmente: vincular la tabla con una columna booleana IsPrimary. Unirse requiere algo como ON (c.computer_id = l.computer_id AND l.is_primary = 1). Funciona, pero se siente mal porque no es fácil restringir los datos para que solo tengan un usuario principal por computadora.

2) Un campo en la tabla de la computadora que apunta directamente a una fila de usuario, todas las filas en la tabla de usuario representan usuarios no primarios. Esto representa mejor la restricción de uno-primario-por-computadora, pero dificulta la obtención de una lista de usuarios de computadoras.

3) Un campo en la tabla de la computadora que enlaza a una fila en la tabla de enlaces. Se siente extraño ...

4) ¿Algo más?

¿Cuál es la forma "relacional" de describir esta relación?

EDITAR: @Mark Brackett: La tercera opción me parece mucho menos extraña ahora que ha demostrado lo agradable que se ve. Por alguna razón, ni siquiera pensé en usar una clave foránea compuesta, así que pensé que tendría que agregar una columna de identidad en la tabla de enlaces para que funcione. Se ve genial, gracias!

@ j04t: Genial, estoy contento de que estemos de acuerdo en el # 3 ahora.

+0

por eso que es difícil enumerar el usuario del ordenador en el punto 2? – albertein

+0

Porque la tabla de enlaces no contiene todos los usuarios de una computadora determinada. –

Respuesta

7

La opción 3, aunque pueda parecer extraña, es la más cercana a la que desea modelar. Se podría hacer algo como:

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

que le da 0 o 1 usuario principal (la PrimaryUserId puede ser anulable si lo desea), que debe estar en Computer_User. Editar: si un usuario solo puede ser primario para 1 computadora, entonces una RESTRICCIÓN ÚNICA en Computer.PrimaryUserId hará cumplir eso.Tenga en cuenta que no hay ningún requisito de que todos los usuarios sean primarios en alguna computadora (eso sería una relación 1: 1, y pediría que estuvieran en la misma tabla).

Editar: Algunas consultas que le muestre la simplicidad de este diseño

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

El equipo FK -> CompUser exige que el usuario principal de una computadora sea uno de los usuarios de esa computadora. La estricción es buena :) – helios

+0

Gracias por la publicación. Me hizo mirar la relación de una manera que inicialmente parece extraña, pero realmente funciona bien. –

+0

¡Me gustó! Desde un punto de vista teórico, aquí estamos diciendo que tenemos una relación "uno a muchos" llamada "es el usuario principal de la computadora ..." además de la relación "muchos a muchos" llamada "es un usuario de computadora ". ¿Alguien vio alguna limitación a esta propuesta? –

0

Dado que el usuario principal es una función de la computadora y el usuario, yo tendería a ir con su enfoque de tener el usuario principal como una columna en la tabla de enlaces.

La otra alternativa que se me ocurre es tener una columna de usuario principal directamente en la mesa de la computadora.

0

Habría creado otra tabla PRIMARY_USERS con unique en computer_id y haciendo ambas claves externas de USUARIOS computer_id y user_id.

0

Cualquiera de las soluciones 1 o 2 funcionará. En este punto me preguntaría con qué será más fácil trabajar. He usado ambos métodos en diferentes situaciones, aunque en general iría con una bandera en la tabla de enlaces y luego forzaría una restricción única en computer_id y isPrimaryUser, de esta manera se asegurará de que cada computadora tenga solo un usuario principal.

+0

a excepción de la falla obvia de usuarios no primarios que tienen valores duplicados de computer_id, isPrimaryUser .... –

+0

Simplemente implemente una segunda clave única para que user_id y isPrimaryUser también sean únicos. Mantiene todo en casa en una sola mesa. –

+0

IsPrimaryUser puede ser 0 o 1. Por lo tanto, su clave única (UserId, IsPrimary) tendría un usuario * como máximo * 1 usuario no primario (UserId, IsPrimary = 0) y 1 usuario principal (UserId, IsPrimary = 1) . Su (ComputerId, IsPrimary) tiene un defecto similar. –

0

2 me parece correcto, pero probaría 1, 2 y 3 por el rendimiento en el tipo de consultas que normalmente realiza y el tipo de volúmenes de datos que tiene.

Como regla general, tiendo a pensar que donde hay una variedad de implementaciones debe mirar a los requisitos de su consulta y diseñar su esquema para obtener el mejor rendimiento y utilización de recursos en el caso más común.

En la rara situación en la que tiene casos igualmente comunes que sugieren implementaciones opuestas, utilice la navaja de Occam.

+0

No estoy de acuerdo con que tenga muchas opciones cuando se trata de modelado relacional. * Hay * un camino correcto y otro incorrecto. La forma incorrecta puede funcionar mejor, pero será un problema de mantenimiento. A veces, tenemos que hacer ese intercambio, pero debe ser una elección consciente. –

+0

Solo para realizar un seguimiento: la opción 2 es * incorrecta * porque permitiría a un usuario ser un usuario principal (al estar en Computer.PrimaryUserId) y un usuario no primario (al estar en Computer_User) para la misma computadora al mismo hora. –

1

Editar - No pensé bien al respecto las primeras 3 veces hasta ...Yo voto por - (solución Número 3)

Usuarios

user id (pk) 

Computadoras

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

los usuarios de ordenadores

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

Ésta es la mejor solución que se me ocurre .

¿Por qué me gusta este diseño.

1) Como se trata de una relación que involucra computadoras y usuarios, me gusta la idea de poder asociar un usuario a varias computadoras como el usuario principal. Sin embargo, esto nunca puede ocurrir donde se usa esta base de datos.

2) La razón por la que no me gusta tener el primary_user en la mesa de enlace

(computer_users.primary_user_id fk-> users.user_id) 

es evitar que un equipo nunca tener múltiples usuarios primarios.

Dadas esas razones, la solución del Número 3 se ve mejor, ya que nunca se encontrará con algunos problemas posibles que veo con los otros enfoques.

Problema de la solución 1: es posible tener varios usuarios principales por computadora.

Problema de la solución 2: la computadora se conecta a un usuario primario cuando la computadora y el usuario no están vinculados entre sí.

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

Solución 3 problema - Parece un poco extraño ¿no? Aparte de eso, no puedo pensar en nada.

Solución 4 problema - No se me ocurre otra forma de hacerlo.


Esta es la cuarta edición, así que espero que todavía tenga sentido.

+0

Esto todavía no resuelve el problema de determinar un usuario "primario". – Mark

0

Tenemos una situación similar en la aplicación en la que trabajo donde tenemos Cuentas que pueden tener muchos Clientes conectados pero solo uno debe ser el Cliente principal.

Usamos una tabla de enlaces (como la tiene) pero tenemos un valor de Secuencia en la tabla de enlaces. El usuario primario es el que tiene Secuencia = 1. Luego, tenemos un índice en esa tabla de enlaces para AccountID y Sequence para asegurarnos de que la combinación de AccountID y Sequence sea única (asegurando que no haya dos clientes principales) en una cuenta). Entonces tendría:

LEFT JOIN c.computer_id = l.computer_id AND l.sequence = 1 
Cuestiones relacionadas