5

Supongamos que tengo las siguientes tablas:SQL: La normalización de la base de datos, manteniendo las restricciones

 ____________________    ____________________ 
    |  Organisms  |   |  Species  | 
    |--------------------|   |--------------------| 
    |OrganismId (int, PK)|   |SpeciesId (int, PK) | 
    |SpeciesId (int, FK) |∞---------1|Name (varchar)  | 
    |Name (varchar)  |   |____________________| 
    |____________________|      1 
       1         | 
       |         | 
       |         | 
       ∞         ∞ 
    ______________________  ____________________   _______________ 
    | OrganismPropsValues |  | SpeciesProps  |  |  Props  | 
    |----------------------|  |--------------------|  |---------------| 
    |OrganismId (int, FK) |  |PropId (int,PK,FK) | ∞-----1|PropId (int,PK)| 
    |PropId (int, FK)  |  |SpeciesId(int,PK,FK)|  |Name (varchar) | 
    |Value (varchar)  |  |____________________|  |_______________| 
    |______________________|            1 
       ∞               | 
       |               | 
       ----------------------------------------------------------- 

Una explicación rápida de lo que estoy tratando de representar aquí: Supongamos que tenemos una lista de especies, tales como gato, perro , humanos, etc. También tenemos un conjunto de propiedades (Apoyos abreviados para que se ajusten más fácilmente en el diagrama) que se aplican a algunas especies, pero no necesariamente a todas; por ejemplo, puede ser la longitud de la cola (para especies con colas)), color de ojos (para aquellos con ojos), etc.

SpeciesProps es una tabla de engarce que define qué propiedades se aplican a qué especie-- así que aquí Tiene {Human, Eye Color}, {Dog, Eye Color}, {Cat, Eye Color}, {Dog, Tail Length}, {Cat, Tail Length}. No tenemos {Human, Tail Length} porque la longitud de cola obviamente no es una propiedad válida para aplicar a un ser humano.

La tabla de Organismos contiene "implementaciones" reales de la especie-- Así que aquí podríamos tener {Humanos, Bob}, {Perro, Rufus}, y {Gato, Felix}.

Aquí está mi problema: en la tabla OrganismPropsValues, quiero almacenar los 'valores' de las propiedades para cada organismo; por ejemplo, para Bob quiero almacenar {Bob, Eye Color, Blue}. Para Rufus, me gustaría almacenar {Rufus, Eye Color, Brown} y {Rufus, Tail Length, 20} (similar para Felix). Mi problema, sin embargo, es que en el esquema que he detallado, es perfectamente posible almacenar {Bob, Tail Length, 10}, aunque la tupla {Human, Tail Length} no exista en SpeciesProps. ¿Cómo puedo modificar este esquema para poder aplicar las restricciones definidas en SpeciesProps en OrganismPropsValues, manteniendo una normalización adecuada?

+0

Dependiendo de la base de datos (por ejemplo Oracle) solo crearía algunos procedimientos almacenados para INSERT/UPDATE/DELETE e implementaría cualquier restricción compleja allí ... – Yahia

+0

@Yahia gracias por la sugerencia, pero si hay una forma para hacer esto sin introducir procedimientos, activadores, etc. Preferiría eso. Esto es MS-SQL (2008). – Andrew

+0

Eso me hizo doler la cabeza.Esto será horrible de consultar (¡piense cuántas combinaciones se necesitarán para obtener todos los datos sobre un humano!) Y es un diseño tan pobre que no sé por dónde empezar. Las bases de datos no son objetos y no deberían diseñarse como objetos. Las tablas EAV son una solución extremadamente pobre. Contrate a un diseñador de base de datos real. – HLGEM

Respuesta

4

Estás aplicación del antipatrón Entity-Attribute-Value. Esto no puede ser un diseño de base de datos normalizado, porque no es relacional.

Lo que sugeriría su lugar es el patrón de diseño Class Table Inheritance:

  • Crear una tabla para los organismos, que contiene propiedades comunes a todas las especies.
  • Cree una tabla por especie, que contenga propiedades específicas de esa especie. Cada una de estas tablas tiene una relación de 1 a 1 con Organismos, pero cada propiedad pertenece a su propia columna.

    ____________________    ____________________ 
    |  Organisms  |   |  Species  | 
    |--------------------|   |--------------------| 
    |OrganismId (int, PK)|   |SpeciesId (int, PK) | 
    |SpeciesId (int, FK) |∞---------1|Name (varchar)  | 
    |Name (varchar)  |   |____________________| 
    |____________________| 
          1 
          | 
          | 
          1 
    ______________________ 
    | HumanOrganism  | 
    |----------------------| 
    |OrganismId (int, FK) | 
    |Sex  (enum)  | 
    |Race  (int, FK) | 
    |EyeColor (int, FK) | 
    |....     | 
    |______________________| 
    

Esto quiere decir que va a crear muchas mesas, pero considera esto como una solución de compromiso con los muchos beneficios prácticos para almacenar propiedades de una manera relacional correcta:

  • Usted puede utilizar los datos de SQL tipos apropiadamente, en lugar de tratar todo un varchar de forma libre.
  • Puede usar restricciones o tablas de búsqueda para restringir ciertas propiedades mediante un conjunto predefinido de valores.
  • Puede hacer que las propiedades sean obligatorias (es decir, NO NULAS) o usar otras restricciones.
  • Los datos y los índices se almacenan de manera más eficiente.
  • Las consultas son más fáciles de escribir y el RDBMS más fácil de ejecutar.

Para más información sobre este diseño, ver el libro de Martin Fowler Patterns of Enterprise Application Architecture, o mi presentación Practical Object-Oriented Models in SQL, o mi libro, SQL Antipatterns: Avoiding the Pitfalls of Database Programming.

+0

Gracias por la sugerencia alternativa ... Examinaré este patrón. – Andrew

2

Hmm ...
Aquí es una manera de hacerlo:
Añadir SpeciesPropsId en la tabla SpeciesProps.
Reemplazar PropId con SpeciesPropsId en la tabla OrganismPropsValues.
Deberá cambiar las restricciones un poco.
Es necesario agregar SpeciesProps a OrganismPropsValues ​​constrain.
Necesidad de eliminar OrganismPropsValues ​​to Props constrain.

Técnicamente no es necesario eliminar PropId de OrganismPropsValues, pero si lo conserva redundará en redundancia de datos.

1

Otra forma de lograr estas restricciones sería cambiar la PK de la tabla Organism dejando caer OrganismId y agregando un No. Luego haga PK el compuesto (SpeciesId, No). Por lo tanto, habría "Bob"(Human, 1), "Rufus" habría (Dog, 1), etc.

A continuación, agregue en la tabla OrganismPropsValues, la SpeciesId y la No (eliminación de la OrganismId.)

Esto permitirá cambiar el FK desde OrganismPropsValuesProps a hacer referencia a SpeciesProps lugar:

 ____________________    ____________________ 
    |  Organisms  |   |  Species  | 
    |--------------------|   |--------------------| 
    |SpeciesId (int, FK) |   |SpeciesId (int, PK) | 
    |No (int)   |∞---------1|Name (varchar)  | 
    |Name (varchar)  |   |____________________| 
    |PK (SpeciedId,No) |      1 
    |____________________|      | 
       1         | 
       |         | 
       |         | 
       ∞         ∞ 
    ______________________  ____________________   _______________ 
    | OrganismPropsValues |  | SpeciesProps  |  |  Props  | 
    |----------------------|  |--------------------|  |---------------| 
    |SpeciesId (int, PK) |  |PropId (int,PK,FK) | ∞-----1|PropId (int,PK)| 
    |No (int, PK)   |  |SpeciesId(int,PK,FK)|  |Name (varchar) | 
    |PropId (int, PK)  |  |____________________|  |_______________| 
    |Value (varchar)  |     1 
    |FK (SpeciesId,No)  |     | 
    |FK (SpeciesId,PropId) |     | 
    |______________________|     | 
       ∞        | 
       |        | 
       ------------------------------- 
+0

Lo que se refiere a @HLGEM para con el problema "EAV" es el campo 'OrganismPropsValues.Value'. No hay una manera fácil de tener verificaciones de integridad en ese campo, ya que puede almacenar diferentes tipos de datos. Por ejemplo, evitas almacenar '{Bob, Tail Length, 10}' con esta estructura db pero no puedes evitar '{Rufus, Tail Length, Blue}' o '{Bob, Eye Color, 20}'. –

+0

Gracias. He oído los conceptos básicos sobre EAV antes, pero nunca he usado uno en la práctica. Este es un proyecto paralelo en el que estoy trabajando, no como si estuviera a punto de ponerlo en el software de producción, pero los datos que trato de modelar aquí parecen exigirlo. Por supuesto, cualquier sugerencia de alternativas es bienvenida; no estoy acostumbrado a usarla. – Andrew

2

Siempre que tenga una dependencia en forma de diamante como esta, considere poner más énfasis en compuesto PRIMARY KEYS.

En concreto, identificar el organismo no sólo por OrganismId, sino por la combinación de SpeciesId y OrganismSubId (que aún puede tener OrganismId, pero mantenerlo como una clave alternativa - no muestran aquí por razones de brevedad).

Una vez hecho esto, el modelo se puede hacer para tener este aspecto:

ER Model

La clave a destacar aquí es que SpeciesId se "propaga" abajo ambos bordes de este diamante gráfico en forma. Esto es lo que le da la restricción deseada de no poder "asignar un valor" a una propiedad que no fue "declarada" para la especie dada.

Por cierto, utilice singular al nombrar sus tablas. Además, considere usar claves primarias naturales (por ejemplo, SpeciesName en lugar de SpeciesId como PK) - si se hace bien, puede aumentar significativamente la velocidad de sus UNIONES (especialmente en conjunción con la agrupación).

Cuestiones relacionadas