2009-03-17 4 views
6

Dada una tabla de modelos 'A', que puede tener varios modelos secundarios 'B', de los cuales 'B' tendrá uno o más modelos secundarios 'C' ... esto suena simple, sin embargo necesito hacer cumplir que para cada 'A', cualquier 'B' debe tener una colección única de 'C' ... por ejemplo, C no puede ser un hijo de dos 'B's que son parte del mismo padre' A '.. pero una' C 'puede ser un hijo de múltiples' B's dado que cada 'padre B' de 'A' es distinto ..Mejor diseño de esquema para relación de tabla que aplica integridad

¿Tiene sentido o debería aclarar mi escenario? ¡salud por adelantado!

Tenga en cuenta que sé que esta política se aplicará en la aplicación, pero quiero que sea imposible que la base de datos esté en un estado no válido.

Editar: Hola a todos, comentarios fantásticos, así que en primer lugar debo agradecerles a todos por compartir sus conocimientos conmigo.

sólo para aclarar la situación, voy a explicar la situación, pero aquí hay algunas notas:

'A' tiene cero o más 'B', una 'B' se asocia implícitamente con una 'A', y como tal es siempre un hijo de solo una 'A'. 'C' es en cierto modo una entidad raíz que está asociada a muchas 'B' así como a otros elementos en la base de datos.


Aquí está la verdadera historia:

Este es un sitio web que contiene muchos escritos (A), y muchos miembros (C), una breve puede tener muchas presentaciones (B), de las cuales una presentación se siempre tiene uno o más miembros asociados. La idea es que una presentación puede de hecho ser una colaboración, y cada miembro no tiene más 'poder' que ningún otro, pero habrá un sistema diferente para validar la política de cómo los miembros trabajan juntos.

Por lo tanto, por escrito, un miembro solo puede enviar un solo envío y un envío puede tener muchos miembros (colaboradores).

Espero que ayude, ¡pero creo que ya me has dado un montón de ayuda!

Steve.

+0

+1 para querer que el DBMS haga cumplir la restricción independientemente de lo que hagan las aplicaciones. –

+0

Estoy con Jonathan en esto, demasiadas personas solo consideran que la aplicación hace este trabajo y tienen malos datos como resultado. – HLGEM

+0

Como lo demuestra mi respuesta, su escenario es bastante ofuscado y si puede modificarlo, debería hacerlo. La cantidad de sistemas que admiten CREATE ASSERTION es muy limitada. La declaración requerida para verificar las restricciones es compleja para usar en un disparador. –

Respuesta

3

Creo que necesita afirmaciones estándar SQL, que son (por desgracia) sin implementarse por DBMS reales.

Todas las respuestas están de acuerdo que hay tres tablas principales llamados TableA, TableB y TableC, cada uno con su propia columna ID:

TableA (A_ID PRIMARY KEY, ...) 
TableB (B_ID PRIMARY KEY, ...) 
TableC (C_ID PRIMARY KEY, ...) 

no es claro por la descripción del problema de si una el único valor B puede tener múltiples entradas A principales. Está claro que una sola C puede tener múltiples entradas principales B. Si un B está ligado a una sola A, el diseño de la Tabla B puede ser revisado para:

TableB (B_ID, ..., A_ID REFERENCES TableA) 

si un B puede estar asociada con varios diferentes Atléticos, a continuación, la conexión está mejor representado por una tabla de unión:

A_and_B (A_ID REFERENCES TableA, 
     B_ID REFERENCES TableB, 
     PRIMARY KEY (A_ID, B_ID) 
     ) 

Tampoco está claro a partir de la descripción si las C asociadas a una B deben ser iguales para cada A con la que B está asociada, o si diferentes A pueden hacer referencia a la misma B, y el conjunto de C asociado con B para A1 puede ser diferente del conjunto de C asociado con B para A2. (Por supuesto, si un solo B solo puede asociarse con un A, este problema es discutible.)

Para los fines de esta respuesta, supongo que cualquier B está asociado con una sola A, por lo tanto la estructura de TableB incluye A_ID como clave externa. Desde una sola C puede estar asociada con varios de B, la estructura relevante es una nueva tabla de unirse:

B_and_C (B_ID REFERENCES TableB, 
     C_ID REFERENCES TableC, 
     PRIMARY KEY (B_ID, C_ID) 
     ) 

Simplificar (omitiendo reglas sobre DEFERRABILITY e inmediatez) una afirmación se ve así:

CREATE ASSERTION assertion_name CHECK (<search_condition>) 

Así Una vez que tenemos un conjunto de decisiones de diseño, podemos escribir una afirmación para validar los datos.Teniendo en cuenta las tablas Tabla A, TableB (con A_ID clave externa), y TableC B_and_C, el requisito es que el número de ocurrencias de un C_ID dada a través de un Una completa es 1.

CREATE ASSERTION only_one_instance_of_c_per_a CHECK 
(
    NOT EXISTS (
     SELECT A_ID, COUNT(C_ID) 
      FROM TableB JOIN B_and_C USING (C_ID) 
      GROUP BY A_ID 
      HAVING COUNT(C_ID) > 1 
    ) 
) 

[ Modificado: Creo que este es más preciso:

CREATE ASSERTION only_one_instance_of_c_per_a CHECK 
(
    NOT EXISTS (
     SELECT A_ID, C_ID, COUNT(*) 
      FROM TableB JOIN B_and_C USING (C_ID) 
      GROUP BY A_ID, C_ID 
      HAVING COUNT(*) > 1 
    ) 
) 

]

el conjunto de condiciones de combinación varía con otras reglas de cómo se conectan las tablas, pero la estructura general de restricción sigue siendo el mismo - no debe existir más de una r eferencia a un C_ID dado para un A_ID particular.


en los comentarios, notas meandmycode:

me da la sensación de que hay un defecto en mi diseño. Mi lógica del mundo real es que una 'B' siempre tiene al menos un niño 'C'. Esto no tiene sentido dado que 'B' debe existir antes de que su hijo pueda unirse. La base de datos permitiría adjuntar una 'B' a una 'A' sin tener al menos UN 'C' .. hijo, como tal voy a revisar 'B' para que tenga un campo que se refiera a su hijo primario 'C', además de tener una colección secundaria de 'C's' adicionales, pero ahora tengo una colección que también podría incluir la 'C' primaria especificada por la 'B', que sería ... incorrecta.

¿Hay un patrón db que infiera una regla de 'uno o más hijos', frente a cero o más?

Creo que tiene problemas con su modelo. Es difícil crear una B si ya debe existir una C que se refiera a la B recién creada, especialmente si las C solo deben referirse a las B existentes. La frase "pollo y huevo" viene a la mente. Entonces, normalmente, permites que B tenga cero o más C en un contexto como este.

Aún no se ha estipulado si TableB tiene una clave externa A_ID o si tiene una tabla de vinculación como A_and_B. Si tiene una clave externa, entonces presumiblemente no puede crear una B hasta que haya creado la A a la que se refiere.

No creo que incluir una ID de C en la tabla B sea una buena idea - hace que el procesamiento sea asimétrico (SQL más difícil). También significa que si necesita eliminar esa C, debe actualizar las cosas para que una de las otras C referencias se elimine de la tabla en la que se encuentra actualmente, y luego actualice el valor en el registro B. Eso es desordenado, ser amable al respecto.

Creo que necesita enmendar su pregunta para definir la estructura de la tabla real que está mirando, a lo largo de las líneas que se muestran en varias respuestas; puede usar puntos triples para representar otras columnas irrelevantes. La afirmación que sugerí probablemente debería implementarse como un tipo de desencadenante, que entra en las anotaciones específicas de DBMS.


partir de la descripción modificada de calzoncillos (A), presentaciones (B) y miembros (C), es evidente que una sola presentación se aplica a sólo un breve, por lo que las comunicaciones pueden tener una clave externa simple que identifica el resumen para el que es una presentación. Y un miembro solo puede colaborar en una presentación para un informe en particular. Habrá una tabla de 'submission_collaborators' con columnas para identificar el envío y el miembro, la combinación es la clave principal y cada columna es una clave externa.

Briefs(Brief_ID, ...) 
Submissions(Submission_ID, Brief_ID REFERENCES Briefs, ...) 
Members(Member_ID, ...) 
Submission_Collaborators(Submission_ID REFERENCES Submissions, 
         Member_ID REFERENCES Members, 
         PRIMARY KEY (Submission_ID, Member_ID) 
         ) 

Por lo tanto, el requisito es que la siguiente consulta debe devolver ninguna fila:

SELECT s.brief_id, c.member_id, COUNT(*) 
    FROM submissions AS s JOIN submission_collaborators AS c 
     ON s.submission_id = c.submission_id 
    GROUP BY s.brief_id, c.member_id 
    HAVING COUNT(*) > 1 

Esta es la misma consulta que I incrustado en la afirmación CREATE (segunda variante). Puede extraer información adicional (título breve, título de envío, nombre de miembro, varias fechas, etc.) también, pero el núcleo del problema es que la consulta mostrada no debe devolver datos.

+0

Jonathan, gracias por la respuesta, es muy conciso ... tengo la sensación de que hay un defecto en mi diseño, mi lógica del mundo real es que una 'B' siempre tiene al menos un hijo 'C' ... esto no tiene sentido dado que 'B' debe existir antes de que su hijo pueda unirse. * cont * – meandmycode

+0

La base de datos permitiría que se adjunte una 'B' a 'A' sin tener al menos UN 'C' .. hijo, como tal voy a revisar 'B' para que tenga un campo que se refiere a su hijo primario 'C', además de tener una colección secundaria de * cont * – meandmycode

+0

'C's adicionales, pero ahora tengo una colección que también podría incluir la' C 'primaria especificada por la' B ', que be .. wrong – meandmycode

0

Agregue la ID de TableA a la Tabla B y agréguela a la clave principal, y haga lo mismo para TableB y TableC.

edición:

Creo que la primera parte de esta respuesta va a trabajar para la restricción de A a B. Sin embargo, yo pondría una tabla de enlace entre B y C que también contenía PK de A. de esa manera tienes un 1: N entre A: B, y tus restricciones se aplican.

+0

Esto no funcionará, porque C puede ser un hijo de múltiples B, siempre y cuando no comparten una @Gamecat A. –

+0

- El la razón por la que dio por no estar funcionando es exactamente la pregunta que estamos tratando de resolver, y esta respuesta es incorrecta, pero es porque no hay nada que impida que todos los Bs tengan C1 como un niño. – cdeszaq

-1

No creo que pueda hacer esto con simples restricciones de integridad referencial declarativa. La mejor forma de hacer cumplir la lógica podría ser el uso de desencadenantes para implementar las restricciones comerciales y deshacer cualquier inserción o actualización que viole las reglas.

+0

Esto realmente se puede hacer. Vea abajo. Los desencadenantes solo enturbian el agua y causan problemas si no están bien documentados. – cdeszaq

+0

@cdeszaq: ¿con qué DBMS lo harías? ¿Cómo lo harías sin un activador? –

0

Lo que tiene es relación ternaria. Lo que debes hacer es tener una tabla que vincule A y B y C en su clave principal. Como las claves primarias no se pueden duplicar, se exigirá que solo haya una C para cada A, y también cada B. Esto creará la colección única que estaba buscando.

se obtiene el siguiente estructura de la tabla:

A's({A_ID}, ...) 
B's({B_ID}, ...) 
C's({C_ID}, ...) 
A_B_C_Relation({[A_ID], [B_ID], [C_ID]}, ...) 

Las claves primarias están en las llaves, claves externas están entre paréntesis.

Mire here para obtener más información.

+0

¿Cómo se aplica esto "C no puede ser un niño de dos 'B's que son parte del mismo padre' A '"? – Jon

+0

Esto no es lo suficientemente restrictivo: permite {A = 1, B = 1, C = 1} y {A = 1, B = 2, C = 1} pero las reglas dicen que no está permitido. –

3

Creo que he capturado aquí su modelo de relación; Si no, entonces yo voto por unobfuscation:

  • A [{AYUDA}, ...]
  • B [{} BID, AID, ...]
  • C [{CID}, .. .]
  • B_C_Link [{BID, CID}, AID]
    • adicional índice único en (AID, CID)

La notación utiliza el {} indicador de clave principal. Entonces, como puede tener múltiples Bs (al poner un AID en B), Bs puede tener Cs (usando una tabla de muchos a muchos B_C_Link), y múltiples Cs no pueden pertenecer a la misma A (al agregar el AID a los muchos- a-muchos mesa y hacer cumplir (AID, CID) singularidad.

+0

Tiene una redundancia en la tabla B_C_Link. La AID se puede encontrar a partir del valor de BID, por lo que su tabla no está en BCNF. De hecho, creo que es solo en 1NF, ni siquiera 2NF ya que el BID -> AID es una dependencia transitiva. Si acepta esa redundancia, entonces el índice único adicional funciona relativamente limpiamente. –

+0

Correcto, esto implica desnormalizar la base de datos. Como dicen "Normalizar hasta que duela, denormalizar hasta que funcione". –

Cuestiones relacionadas