5

Perdón por el título, es difícil de explicar.MySQL: Dos relaciones n: 1, pero no ambas a la vez

necesito un modelo de datos similar a esto: alt text

Como se puede ver, un conjunto puede pertenecer tanto a un usuario o una escuela. Mi problema: solo se debe permitir que pertenezca a un usuario O a una escuela. Pero nunca ambos al mismo tiempo.

¿Cómo puedo resolver este problema?

+0

¿Qué usaste para hacer el gráfico? – basszero

+0

@basszero: MySQL Workbench –

+0

wow! ¡Ha recorrido un largo camino desde la última vez que lo probé! – basszero

Respuesta

6

Su diseño actual se llama arcos exclusivos donde la mesa sets tiene dos claves externas, y necesita exactamente uno de ellos para ser no nulo. Esta es una forma de implementar asociaciones polimórficas, ya que una clave externa dada puede hacer referencia solo a una tabla de objetivos.

Otra solución es hacer una "supertable" común que las referencias users y schools, y luego usar eso como el padre de sets.

create table set_owner 

create table users 
    PK is also FK --> set_owner 

create table schools 
    PK is also FK --> set_owner 

create table sets 
    FK --> set_owner 

Usted puede pensar en esto como algo análogo a una interfazen el modelado OO:

interface SetOwner { ... } 

class User implements SetOwner { ... } 

class School implements SetOwner { ... } 

class Set { 
    SetOwner owner; 
} 

Re sus comentarios:

Así la tabla SetOwner contiene ambos identificadores de usuario y SchoolIDs, ¿correcto? Eso significaría que no se me permitiría tener la misma identificación para un usuario y una escuela. ¿Cómo puedo hacer cumplir esto?

Deje que la tabla SetOwners genere valores de id. Debe insertarlo en SetOwners antes de poder insertarlo en Usuarios o Escuelas. Por lo tanto, realice las identificaciones en Usuarios y escuelas no autoincrementando; sólo tiene que utilizar el valor que se genera por SetOwners:

INSERT INTO SetOwners DEFAULT VALUES; -- generates an id 
INSERT INTO Schools (id, name, location) VALUES (LAST_INSERT_ID(), 'name', 'location'); 

De esta manera el valor ID NO dada será utilizado tanto para una escuela y un usuario.

Si me gustaría obtener el tipo de propietario para un conjunto, ¿necesito un atributo ownerType en mi tabla de SetOwner?

Sin duda puede hacer esto. De hecho, puede haber otras columnas que sean comunes tanto para los usuarios como para las escuelas, y usted podría poner estas columnas en el supertable SetOwners. Esto entra en el patrón de Martin Fowler Class Table Inheritance.

Y si quiero obtener el nombre de la escuela o el usuario (cualquiera que sea el tipo), puedo hacer esto con una sola consulta, o necesito dos consultas (primero para obtener el tipo y segundo para obtener el nombre)?

Tienes que hacer una unión. Si realiza una consulta desde un conjunto determinado y sabe que pertenece a un usuario (no a una escuela) puede omitir unirse a SetOwners y unirse directamente a los usuarios. Las uniones no tienen necesariamente que pasar por claves externas.

SELECT u.name FROM Sets s JOIN Users u ON s.SetOwner_id = u.id WHERE ... 

Si no sabe si un determinado conjunto pertenece a un usuario o una escuela, tendría que hacer una combinación externa a la vez:

SELECT COALESCE(u.name, sc.name) AS name 
FROM Sets s 
LEFT OUTER JOIN Users u ON s.SetOwner_id = u.id 
LEFT OUTER JOIN Schools sc ON s.SetOwner_id = sc.id 
WHERE ... 

Usted sabe que el SetOwner_id debe relacione una u otra tabla, Usuarios o Escuelas, pero no ambas.

+0

Gracias por la gran explicación. El enfoque con el supertable parece similar al que geofflane propuso en su comentario. Creo que entiendo el concepto ahora. Entonces, la tabla 'SetOwner' contiene tanto UserIDs como SchoolIDs, ¿correcto? Eso significaría que no se me permitiría tener la misma identificación para un usuario y una escuela. ¿Cómo puedo hacer cumplir esto? –

+0

Y otro aspecto: el ERD actualmente se ve así http://imgur.com/vD1ln.png.Si me gustaría obtener el tipo de propietario para un conjunto, ¿necesito un atributo ownerType en mi tabla de SetOwner? ¿O hay otra forma de hacerlo? Y si quiero obtener el nombre de la escuela o del usuario (cualquiera que sea el tipo), ¿puedo hacer esto con una sola consulta o necesito dos consultas (la primera para obtener el tipo y la segunda para obtener el nombre)? –

+0

Impresionante edición, gracias. Es por eso que amo Stackoverflow. Solo queda una pregunta: ¿Puedo usar 'LAST_INSERT_ID()' de forma segura en un entorno multiusuario, o pueden las consultas y las inserciones de otros usuarios interferir con la consulta actual, de modo que el LAST_INSERT_ID cambie? –

1

Deberá usar un desencadenador para hacer cumplir esta regla comercial, porque MySQL tiene pero no impone restricciones CHECK (en cualquier motor).

2

Con la advertencia: no está del todo claro qué es un conjunto en su modelo de datos.

Estoy cuestionando su modelo de datos.

¿Por qué el usuario -> Conjuntos y Escuela -> Conjuntos tienen que apuntar a la misma tabla. No está más Normalizado si esos son realmente conjuntos independientes de datos. El hecho de que compartan algunas similitudes en las columnas que rastrean no significa que se deben almacenar en la misma tabla. ¿Son lógicamente lo mismo?

Solo crea tablas separadas para School Sets y User Sets. Esto también facilitará las consultas inversas porque no tendrá que verificar las relaciones nulas en la tabla Conjuntos para saber si realmente se trata de un conjunto de usuarios o un conjunto de escuelas.

+0

Gracias por la pista. De hecho, los sets (muchos vocabs hacen un set) son exactamente iguales, solo pueden tener diferentes propietarios, ya sea un colegio o un usuario. Como un conjunto nunca pasará de pertenecer a una escuela a pertenecer a un usuario o viceversa, podría simplemente crear dos tipos de conjuntos y copiar los datos a la otra tabla cuando sea necesario. El problema con este enfoque es que necesitaré dos tipos de vocabs, y realmente no creo que tenga mucho sentido. –

+0

Probablemente podrías poner una tabla de SetOwner entre Usuarios/Escuelas y Conjuntos. Entonces, cada una de las tablas que podrían 'poseer' un conjunto tendría una columna FK para un set_owner_id. La tabla Set también tendría un set_owner_id. Entonces, básicamente, un usuario y una escuela tienen una relación 1: 1 con SetOwner y SetOwner tiene 1: m con Set. Entonces todo tiene integridad referencial exigible. Eso hará que sea bastante difícil pasar de Establecer al Propietario (lógico) sin embargo. – geofflane

+0

¿Te refieres a esto? http://i.imgur.com/dVRs9.png Realmente no entiendo cómo esto resuelve el problema. –

Cuestiones relacionadas