No sé cómo transformar una entidad variable en una tabla relacional.

9

INTRODUCCIÓN E INFORMACIÓN RELEVANTE:

El siguiente ejemplo ilustra el problema que enfrento:

El animal tiene una raza, que puede ser un gato o un perro . El gato puede ser siamés o persa . El perro puede ser un pastor alemán o un labrador retriver .

Animal es una entidad fuerte, mientras que su raza es un atributo que puede tener uno de los dos valores ofrecidos (gato o perro). Ambos valores son complejos (he agregado aquí solo el tipo de perro / gato para ilustrar el problema, pero también puede haber el nombre del gato / perro y un montón de otras cosas).

PROBLEMA:

No sé cómo crear tablas relacionales para este ejemplo.

MIS ESFUERZOS PARA RESOLVER EL PROBLEMA:

He intentado dibujar un diagrama ER, usando la notación de Chen, que representa el problema, pero como soy un principiante, no sé si lo hice bien. Esto es lo que tengo:

ingrese la descripción de la imagen aquí

Pido disculpas si dibujé algo mal, corrígeme si ese es el caso. No deseo simplemente obtener una "solución gratuita" sino también aprender a lidiar con este problema para poder resolverlo por mi cuenta en el futuro.

Lo único que se me ocurre es crear dos tablas separadas, una para gatos y otra para perros. Además, el atributo de raza en la tabla Animal solo almacenaría el valor de un gato o un perro . Algo como esto:

Animal< # Animal_ID, race, other attributes >
Cat < # Cat_ID, $ Animal_ID, breed >
Dog < # Dog_ID, $ Animal_ID, breed >

Realmente tengo un mal presentimiento sobre mi solución y me temo que está mal, de ahí la siguiente pregunta.

PREGUNTAS

  • ¿Cómo puedo transformar mi ejemplo en un diagrama ER?
  • ¿Cómo transformar ese diagrama ER en tablas relacionales?

Si se requiere más información, deje un comentario y actualizaré mi publicación lo antes posible. También siéntase libre de agregar etiquetas apropiadas ya que soy bastante nuevo aquí.

Gracias.

Siempre aprendiendo nuevo
fuente
1
La transformación de los diagramas EER a tablas se puede encontrar en este artículo de 1986 de TJTeorey, D.Yang, JPFry: una metodología de diseño lógico para bases de datos relacionales utilizando el modelo extendido de entidad-relación . Es sencillo y uno de mis papeles favoritos.
milagro173

Respuestas:

11

La estructura adecuada para este escenario es un modelo de Subclase / Herencia, y es casi idéntico al concepto que propuse en esta respuesta: Lista de valores ordenada heterogénea .

El modelo propuesto en esta pregunta es en realidad bastante cercano, ya que la Animalentidad contiene el tipo (es decir race) y las propiedades que son comunes en todos los tipos. Sin embargo, hay dos cambios menores que se necesitan:

  1. Elimine los campos Cat_ID y Dog_ID de sus respectivas entidades:

    El concepto clave aquí es que todo lo que es una Animal, sin tener en cuenta race: Cat, Dog, Elephant, y así sucesivamente. Dado ese punto de partida, cualquier particular racede Animalrealmente no necesita un identificador separado ya que:

    1. el Animal_IDes único
    2. the Cat, Dogy cualquier raceentidad adicional agregada en el futuro, por sí mismas, no representan completamente ningún particular Animal; que sólo tienen significado cuando se utiliza en combinación con la información contenida en la entidad matriz, Animal.

    Por lo tanto, la Animal_IDpropiedad en el Cat, Dog, etc entidades es a la vez el PK y la parte posterior FK a la Animalentidad.

  2. Diferenciar entre tipos de breed:

    El hecho de que dos propiedades compartan el mismo nombre no significa necesariamente que esas propiedades sean las mismas, incluso si el nombre es el mismo implica tal relación. En este caso, lo que realmente tiene es en realidad CatBreedy DogBreedcomo "tipos" separados

Notas iniciales

  1. El SQL es específico de Microsoft SQL Server (es decir, es T-SQL). Es decir, tenga cuidado con los tipos de datos, ya que no son los mismos en todos los RDBMS. Por ejemplo, lo estoy usando, VARCHARpero si necesita almacenar algo fuera del conjunto ASCII estándar, realmente debería usarlo NVARCHAR.
  2. Los campos de ID de las tablas de "tipo" ( Race, CatBreedy DogBreed) no se incrementan automáticamente (es decir, IDENTITY en términos de T-SQL) porque son constantes de aplicación (es decir, son parte de la aplicación) que son valores de búsqueda estáticos en base de datos y se representan como enums en C # (u otros lenguajes). Si se agregan valores, se agregan en situaciones controladas. Me reservo el uso de campos de incremento automático para los datos del usuario que ingresan a través de la aplicación.
  3. La convención de nomenclatura que uso es nombrar cada tabla de subclase comenzando con el nombre de la clase principal seguido del nombre de la subclase. Esto ayuda a organizar las tablas y también indica claramente (sin mirar las FK) la relación de la tabla de subclase con la tabla de entidad principal.
  4. Consulte la sección "Edición final" al final para obtener una nota sobre Vistas.

"Raza" como "Raza" - Enfoque específico

Raza como diagrama específico de la raza
Este primer conjunto de tablas son las tablas de búsqueda / tipos:

CREATE TABLE Race
(
  RaceID INT NOT NULL PRIMARY KEY
  RaceName VARCHAR(50) NOT NULL
);

CREATE TABLE CatBreed
(
  CatBreedID INT NOT NULL PRIMARY KEY,
  BreedName VARCHAR(50),
  CatBreedAttribute1 INT,
  CatBreedAttribute2 VARCHAR(10)
  -- other "CatBreed"-specific properties as needed
);

CREATE TABLE DogBreed
(
  DogBreedID INT NOT NULL PRIMARY KEY,
  BreedName VARCHAR(50),
  DogBreedAttribute1 TINYINT
  -- other "DogBreed"-specific properties as needed
);

Este segundo listado es la principal entidad "Animal":

CREATE TABLE Animal
(
  AnimalID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  RaceID INT NOT NULL, -- FK to Race
  Name VARCHAR(50)
  -- other "Animal" properties that are shared across "Race" types
);

ALTER TABLE Animal
  ADD CONSTRAINT [FK_Animal_Race]
  FOREIGN KEY (RaceID)
  REFERENCES Race (RaceID);

Este tercer conjunto de tablas son las entidades de subclase complementarias que completan la definición de cada una Racede Animal:

CREATE TABLE AnimalCat
(
  AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
  CatBreedID INT NOT NULL, -- FK to CatBreed
  HairColor VARCHAR(50) NOT NULL
  -- other "Cat"-specific properties as needed
);

ALTER TABLE AnimalCat
  ADD CONSTRAINT [FK_AnimalCat_CatBreed]
  FOREIGN KEY (CatBreedID)
  REFERENCES CatBreed (CatBreedID);

ALTER TABLE AnimalCat
  ADD CONSTRAINT [FK_AnimalCat_Animal]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal (AnimalID);


CREATE TABLE AnimalDog
(
  AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
  DogBreedID INT NOT NULL, -- FK to DogBreed
  HairColor VARCHAR(50) NOT NULL
  -- other "Dog"-specific properties as needed
);

ALTER TABLE AnimalDog
  ADD CONSTRAINT [FK_AnimalDog_DogBreed]
  FOREIGN KEY (DogBreedID)
  REFERENCES DogBreed (DogBreedID);

ALTER TABLE AnimalDog
  ADD CONSTRAINT [FK_AnimalDog_Animal]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal (AnimalID);

El modelo que usa un breedtipo compartido se muestra después de la sección "Notas adicionales".

Notas adicionales

  1. El concepto de breedparece ser un punto focal para la confusión. Fue sugerido por jcolebrand (en un comentario sobre la pregunta) que breedes una propiedad compartida entre los diferentes correos electrónicos race, y las otras dos respuestas lo tienen integrado como tal en sus modelos. Sin embargo, esto es un error, porque los valores de breedno se comparten entre los diferentes valores de race. Sí, soy consciente de que los otros dos modelos propuestos intentan resolver este problema haciendo raceun padre de breed. Si bien eso resuelve técnicamente el problema de la relación, no ayuda a resolver la pregunta general de modelado sobre qué hacer con las propiedades no comunes, ni cómo manejar una raceque no tiene a breed. Pero, en el caso de que se garantizara que tal propiedad existiera en todosAnimals, también incluiré una opción para eso (a continuación).
  2. Los modelos propuestos por vijayp y DavidN (que parecen ser idénticos) no funcionan porque:
    1. Ellos tampoco
      1. no permita que se almacenen propiedades no comunes (al menos no para instancias individuales de ninguna Animal), o
      2. requieren que todas las propiedades de todos los races se almacenen en la Animalentidad, que es una forma muy plana (y casi no relacional) de representar estos datos. Sí, la gente hace esto todo el tiempo, pero significa tener muchos campos NULL por fila para las propiedades que no están destinadas a ese particular raceY saber qué campos por fila están asociados con el particular racede ese registro.
    2. No permiten agregar una racede Animalen el futuro que no tiene breedcomo propiedad. E incluso si TODOS Animaltienen una breed, eso no cambiaría la estructura debido a lo que se ha señalado anteriormente breed: eso breeddepende de race(es decir, breedporque Catno es lo mismo que breedpara Dog).

"Raza" como enfoque de propiedad común / compartida

ingrese la descripción de la imagen aquí
Tenga en cuenta:

  1. El SQL a continuación se puede ejecutar en la misma base de datos que el modelo presentado anteriormente:

    1. La Racemesa es igual
    2. La Breedmesa es nueva
    3. Las tres Animaltablas han sido agregadas con un2
  2. Incluso Breedsiendo una propiedad ahora común, no parece correcto no haberlo Racenotado en la entidad principal / principal (incluso si es técnicamente correcto desde el punto de vista relacional). Entonces, ambos RaceIDy BreedIDestán representados en Animal2. Para evitar una falta de coincidencia entre lo RaceIDanotado en Animal2y lo BreedIDque es para un diferente RaceID, he agregado un FK en ambos RaceID, BreedIDque hace referencia a una RESTRICCIÓN ÚNICA de esos campos en la Breedtabla. Por lo general, desprecio señalar un FK a una RESTRICCIÓN ÚNICA, pero esta es una de las pocas razones válidas para hacerlo. Una RESTRICCIÓN ÚNICA es lógicamente una "Clave alternativa", que la hace válida para este uso. Tenga en cuenta también que la Breedtabla todavía tiene un PK en solo BreedID.
    1. La razón para no ir solo con un PK en los campos combinados y sin RESTRICCIONES ÚNICAS es que permitiría BreedIDque se repita lo mismo en diferentes valores de RaceID.
    2. La razón para no cambiar la PK y la RESTRICCIÓN ÚNICA es que este podría no ser el único uso de BreedID, por lo que aún debería ser posible hacer referencia a un valor específico de Breedsin tener RaceIDdisponible.
  3. Si bien el siguiente modelo funciona, tiene dos fallas potenciales con respecto al concepto de un compartido Breed(y es por eso que prefiero las tablas Raceespecíficas Breed).
    1. Hay una suposición implícita de que TODOS los valores de Breedtienen las mismas propiedades. No hay una manera fácil en este modelo de tener propiedades dispares entre Dog"razas" y Elephant"razas". Sin embargo, todavía hay una manera de hacerlo, que se observa en la sección "Edición final".
    2. No hay forma de compartir una Breeden más de una carrera. No estoy seguro de si es deseable hacerlo (o tal vez no en el concepto de animales, pero posiblemente en otras situaciones que estarían usando este tipo de modelo), pero aquí no es posible.
CREATE TABLE Race
(
  RaceID INT NOT NULL PRIMARY KEY,
  RaceName VARCHAR(50) NOT NULL
);

CREATE TABLE Breed
(
  BreedID INT NOT NULL PRIMARY KEY,
  RaceID INT NOT NULL, -- FK to Race
  BreedName VARCHAR(50)
);

ALTER TABLE Breed
  ADD CONSTRAINT [UQ_Breed]
  UNIQUE (RaceID, BreedID);

ALTER TABLE Breed
  ADD CONSTRAINT [FK_Breed_Race]
  FOREIGN KEY (RaceID)
  REFERENCES Race (RaceID);

CREATE TABLE Animal2
(
  AnimalID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  RaceID INT NOT NULL, -- FK to Race, FK to Breed
  BreedID INT NOT NULL, -- FK to Breed
  Name VARCHAR(50)
  -- other properties common to all "Animal" types
);

ALTER TABLE Animal2
  ADD CONSTRAINT [FK_Animal2_Race]
  FOREIGN KEY (RaceID)
  REFERENCES Race (RaceID);

-- This FK points to the UNIQUE CONSTRAINT on Breed, _not_ to the PK!
ALTER TABLE Animal2
  ADD CONSTRAINT [FK_Animal2_Breed]
  FOREIGN KEY (RaceID, BreedID)
  REFERENCES Breed (RaceID, BreedID);


CREATE TABLE AnimalCat2
(
  AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
  HairColor VARCHAR(50) NOT NULL
);

ALTER TABLE AnimalCat2
  ADD CONSTRAINT [FK_AnimalCat2_Animal2]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal2 (AnimalID);

CREATE TABLE AnimalDog2
(
  AnimalID INT NOT NULL PRIMARY KEY,
  HairColor VARCHAR(50) NOT NULL
);

ALTER TABLE AnimalDog2
  ADD CONSTRAINT [FK_AnimalDog2_Animal2]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal2 (AnimalID);


Edición final (con suerte ;-)

  1. Con respecto a la posibilidad (y luego la dificultad) de manejar propiedades dispares entre tipos de Breed, es posible emplear el mismo concepto de subclase / herencia pero con Breedla entidad principal. En esta configuración, la Breedtabla tendría las propiedades comunes a todos los tipos de Breed(al igual que la Animaltabla) y RaceIDrepresentaría el tipo de Breed(igual que en la Animaltabla). De allí tendría que tener tablas de subclase como BreedCat, BreedDogy así sucesivamente. Para proyectos más pequeños esto podría considerarse "sobre-ingeniería", pero se menciona como una opción para situaciones que se beneficiarían de él.
  2. Para ambos enfoques, a veces ayuda crear Vistas como accesos directos a las entidades completas. Por ejemplo, considere:

    CREATE VIEW Cats AS
       SELECT  an.AnimalID,
               an.RaceID,
               an.Name,
               -- other "Animal" properties that are shared across "Race" types
               cat.CatBreedID,
               cat.HairColor
               -- other "Cat"-specific properties as needed
       FROM    Animal an
       INNER JOIN  AnimalCat cat
               ON  cat.AnimalID = an.AnimalID
       -- maybe add in JOIN(s) and field(s) for "Race" and/or "Breed"
  3. Si bien no forma parte de las entidades lógicas, es bastante común tener campos de auditoría en las tablas para al menos tener una idea de cuándo se insertan y actualizan los registros. Entonces, en términos prácticos:
    1. Se CreatedDateagregaría un campo a la Animaltabla. Este campo no es necesario en ninguna de las tablas de subclase (p AnimalCat. Ej. ), Ya que las filas que se insertan para ambas tablas deben realizarse al mismo tiempo dentro de una transacción.
    2. Se LastModifiedDateagregaría un campo a la Animaltabla y a todas las tablas de subclase. Este campo se actualiza solo si esa tabla en particular se actualiza: si se produce una actualización AnimalCatpero no en Animaluna en particular AnimalID, solo se establecerá el LastModifiedDatecampo en AnimalCat.
Solomon Rutzky
fuente
2
De alguna manera tengo la sensación de que entendiste exactamente cuál es mi problema. Echaré un vistazo a su respuesta vinculada y la estudiaré detenidamente. Una simple definición de tablas también sería genial (si las consultas SQL son demasiado para que las escriba en este momento). Si decide actualizar su publicación con consultas SQL o definiciones de tabla, déjeme un comentario. Gracias de nuevo. Atentamente.
AlwaysLearningNewStuff
1
Estoy tratando de aplicar su respuesta a mi caso de la vida real. Si sigo ciegamente sus instrucciones, creo que podría perder la oportunidad de optimizar aún más mi diseño. Me gustaría que eche un vistazo a mi última pregunta, ya que ha podido comprender perfectamente mis preguntas y proporcionar excelentes respuestas. He compuesto la pregunta para usar el modelo de datos genéricos con el fin de ser útil también para futuros lectores. Si tienes problemas para encontrarlo, déjame un comentario. Gracias y perdón por molestar ...
AlwaysLearningNewStuff
@AlwaysLearningNewStuff Hola. Recibí este mensaje antes, pero no tuve tiempo de recibirlo de inmediato. Pude encontrar la nueva Pregunta haciendo clic en su nombre arriba y muestra todas sus Preguntas :-).
Solomon Rutzky
Me refería a esta pregunta . En pocas palabras: tengo 3 entidades con un atributo común D, por lo tanto, quería aplicar el método a partir de su respuesta. Dos entidades tienen un atributo común Eque no está presente en la tercera entidad. ¿Debo ignorar este hecho y aplicar una solución estándar, o hay alguna manera de optimizar aún más mi diseño?
AlwaysLearningNewStuff
4

En primer lugar, está haciendo bien para distinguir entre el modelado ER y el modelado relacional. Muchos novatos no lo hacen.

Aquí hay algunas palabras de moda que puede usar para buscar artículos útiles en la web.

Su caso es un caso clásico de clase / subclase o, si lo desea, escriba / subtipo.

La frase que se usa en el modelado ER es "generalización / especialización". Y muchos de los artículos muestran esto bajo algo llamado modelado EER (Enhanced Entity-Relationship). Esto no estaba en la presentación original de Peter Chen del modelado ER. Fue agregado más tarde. Para obtener un resumen bastante bueno de gen / spec en formato pdf, haga clic aquí

A continuación, al convertir un caso de clase / subclase a modelado relacional, diseña tablas. Hay más de un enfoque. Los dos enfoques principales se denominan herencia de tabla única y herencia de tabla de clase. Cada uno tiene ventajas y desventajas. La mejor presentación de estos dos diseños proviene de Martin Fowler. Puedes ver su esquema aquí y aquí .

La gran ventaja de la herencia de una sola tabla es la simplicidad. Todo está almacenado en una tabla. El gran inconveniente son muchos NULLS. Esto puede desperdiciar espacio y tiempo y resultar en una lógica confusa.

La herencia de la tabla de clase requiere uniones, pero son simples y rápidas. Especialmente si usa una técnica llamada clave primaria compartida, en la que el PK en las tablas de subclase es una copia del PK en la tabla de superclase. Puede crear vistas para cada subclase que unan datos de superclase con datos de subclase.

Finalmente, hay una etiqueta en esta área que recopila preguntas como la suya.
Aquí está:

Walter Mitty
fuente
1
+1 Lo que me confunde es la falta de claves principales en los diagramas de la tabla. Especialmente en el "classTableInheritance" no puedo ver que todas estas tablas estén conectadas por la misma clave primaria.
milagro173
@ miracle173 un punto válido. Por alguna razón, Fowler no incluye las PK y FK en el diagrama. Hay otros artículos en herencia de tabla de clase que proporcionan este detalle. No todas las implementaciones de herencia de tablas de clase lo combinan con la clave primaria compartida. Lo recomiendo. Es un poco más de trabajo en el momento de la inserción, pero más fácil y más rápido en el momento de la recuperación conjunta.
Walter Mitty
3

Veo el posible diseño como

Mesa Race

RaceId- PK- Int
RaceName - Varchar(50)

Mesa Breed

BreedId - PK- Int
RaceId - FK - Int
BreedName - varchar(50)

Mesa Animal

AnimalId - PK- Int
BreedId - FK - Int
Other Columns....

Estas PK anteriores serían columnas de incremento automático. Otras columnas en la Animaltabla podrían nombrarse en consecuencia.

ingrese la descripción de la imagen aquí

vijayp
fuente
Además, agregaría campos con teclas de Raza y Tipo (podrían ser desencadenantes) en la tabla Animal para facilitar índices posteriores para mejorar la velocidad.
Felipe Alcacibar
0

Tu método actual no es malo. Sin embargo, si vas a agregar más razas más tarde (aves, peces, etc.), crear una tabla separada para cada una podría ser engorroso. Recomendaría algo como lo siguiente:

Animal < # Animal_ID, Breed_ID, other attributes >
Breed < # Breed_ID, Race_ID >
Race < # Race_ID >

Una raza, a mi entender, debería tener una sola raza. Entonces, si almacena la raza en la tabla Animal, podrá determinar la raza uniéndose a la tabla Raza. Obviamente, agregue cualquier otro atributo (nombre, descripción, etc.) a las tablas de Raza y Raza según sea necesario.

DavidN
fuente