.net - relacional - herencia entidad relacion
¿Cómo modelas efectivamente la herencia en una base de datos? (9)
¿Cuáles son las mejores prácticas para modelar la herencia en las bases de datos?
¿Cuáles son las compensaciones (por ejemplo, queriability)?
(Estoy más interesado en SQL Server y .NET, pero también quiero entender cómo otras plataformas abordan este problema).
Deberías normalizar tu base de datos y eso realmente reflejaría tu herencia. Puede tener degradación del rendimiento, pero así es con la normalización. Probablemente tendrá que usar el buen sentido común para encontrar el equilibrio.
El diseño adecuado de la base de datos no se parece en nada al diseño de objetos adecuado.
Si planea usar la base de datos para algo más que simplemente serializar sus objetos (como informes, consultas, uso de aplicaciones múltiples, inteligencia comercial, etc.), entonces no recomiendo ningún tipo de asignación simple de objetos a tablas.
Mucha gente piensa en una fila en una tabla de base de datos como una entidad (pasé muchos años pensando en esos términos), pero una fila no es una entidad. Es una proposición. Una relación de base de datos (es decir, tabla) representa una declaración de hechos sobre el mundo. La presencia de la fila indica que el hecho es verdadero (y, a la inversa, su ausencia indica que el hecho es falso).
Con este entendimiento, puede ver que un solo tipo en un programa orientado a objetos puede almacenarse a través de una docena de relaciones diferentes. Y una variedad de tipos (unidos por herencia, asociación, agregación o completamente no afiliados) puede almacenarse parcialmente en una relación única.
Lo mejor es preguntarse qué datos quiere almacenar, a qué preguntas va a querer responder, qué informes quiere generar.
Una vez que se crea el diseño de base de datos apropiado, es una cuestión simple crear consultas / vistas que le permitan serializar sus objetos a esas relaciones.
Ejemplo:
En un sistema de reserva de hotel, es posible que necesite almacenar el hecho de que Jane Doe tiene una reserva para una habitación en el Seaview Inn del 10 al 12 de abril. ¿Es eso un atributo de la entidad del cliente? ¿Es un atributo de la entidad hotelera? ¿Es una entidad de reserva con propiedades que incluyen clientes y hoteles? Podría ser cualquiera o todas esas cosas en un sistema orientado a objetos. En una base de datos, no es ninguna de esas cosas. Es simplemente un hecho simple.
Para ver la diferencia, considere las siguientes dos consultas. (1) ¿Cuántas reservas de hotel tiene Jane Doe para el próximo año? (2) ¿Cuántas habitaciones están reservadas para el 10 de abril en el Seaview Inn?
En un sistema orientado a objetos, la consulta (1) es un atributo de la entidad del cliente, y la consulta (2) es un atributo de la entidad del hotel. Esos son los objetos que expondrían esas propiedades en sus API. (Aunque, obviamente, los mecanismos internos por los cuales se obtienen esos valores pueden implicar referencias a otros objetos).
En un sistema de base de datos relacional, ambas consultas examinarían la relación de reserva para obtener sus números, y conceptualmente no hay necesidad de molestarse con ninguna otra "entidad".
Por lo tanto, es intentando almacenar hechos sobre el mundo, en lugar de intentar almacenar entidades con atributos, que se construya una base de datos relacional adecuada. Y una vez que está diseñado correctamente, las consultas útiles que no se soplaron durante la fase de diseño se pueden construir fácilmente, ya que todos los datos necesarios para cumplir con esas consultas están en su lugar.
Hay dos tipos principales de herencia que puede configurar en un DB, tabla por entidad y tabla por jerarquía.
La tabla por entidad es donde tiene una tabla de entidad base que tiene propiedades compartidas de todas las clases secundarias. Luego tiene una clase por niño, otra tabla, cada una con solo propiedades aplicables a esa clase. Están vinculados 1: 1 por sus PK
texto alternativo http://mattlant.com/ent.png
La tabla por jerarquía es donde todas las clases comparten una tabla, y las propiedades opcionales son anulables. También es un campo discriminador, que es un número que denota el tipo que el registro contiene actualmente
texto alternativo http://mattlant.com/hier.png SessionTypeID es discriminador
El objetivo por jerarquía es más rápido de consultar ya que no necesita uniones (solo el valor del discriminador), mientras que el objetivo por entidad necesita hacer uniones complejas para detectar qué tipo es algo así como recuperar todos sus datos.
Editar: Las imágenes que muestro aquí son capturas de pantalla de un proyecto en el que estoy trabajando. La imagen de Asset no está completa, de ahí su vacío, pero fue principalmente para mostrar cómo se configura, no qué poner dentro de las tablas. Eso depende de ti ;). La tabla de sesión contiene información de la sesión de colaboración virtual, y puede ser de varios tipos de sesiones según el tipo de colaboración involucrada.
Hay varias formas de modelar la herencia en una base de datos. Lo que elija depende de sus necesidades. Aquí hay algunas opciones:
Tabla por tipo (TPT)
Cada clase tiene su propia tabla. La clase base tiene todos los elementos de la clase base, y cada clase que deriva de ella tiene su propia tabla, con una clave primaria que también es una clave externa a la tabla de la clase base; la clase de la tabla derivada contiene solo los diferentes elementos.
Así por ejemplo:
class Person {
public int ID;
public string FirstName;
public string LastName;
}
class Employee : Person {
public DateTime StartDate;
}
Ocasionaría tablas como:
table Person
------------
int id (PK)
string firstname
string lastname
table Employee
--------------
int id (PK, FK)
datetime startdate
Tabla por jerarquía (TPH)
Hay una sola tabla que representa toda la jerarquía de herencia, lo que significa que varias de las columnas probablemente sean dispersas. Se agrega una columna discriminatoria que le dice al sistema qué tipo de fila es esta.
Dadas las clases anteriores, terminas con esta tabla:
table Person
------------
int id (PK)
int rowtype (0 = "Person", 1 = "Employee")
string firstname
string lastname
datetime startdate
Para cualquier fila que sea rowtype 0 (Person), la fecha de inicio siempre será nula.
Table-Per-Concrete (TPC)
Cada clase tiene su propia tabla completamente formada sin referencias a ninguna otra tabla.
Dadas las clases anteriores, terminas con estas tablas:
table Person
------------
int id (PK)
string firstname
string lastname
table Employee
--------------
int id (PK)
string firstname
string lastname
datetime startdate
Los patrones TPT, TPH y TPC son las formas en que puede ir, como lo menciona Brad Wilson. Pero par de notas:
las clases secundarias que heredan de una clase base pueden verse como entidades débiles para la definición de clase base en la base de datos, lo que significa que dependen de su clase base y no pueden existir sin ella. He visto varias veces, que los ID únicos se almacenan para todas las tablas secundarias al mismo tiempo que se mantiene el FK en la tabla padre. Un FK es suficiente y es incluso mejor tener habilitado en cascada la eliminación para la relación FK entre el niño y las tablas base.
En TPT, al solo ver los registros de la tabla base, no puede encontrar qué clase secundaria está representando el registro. Esto a veces es necesario cuando desea cargar una lista de todos los registros (sin
select
en cada una de las tablas secundarias). Una forma de manejar esto, es tener una columna que represente el tipo de la clase secundaria (similar al campo rowType en el TPH), por lo que se mezclan el TPT y el TPH de alguna manera.
Digamos que queremos diseñar una base de datos que contenga el siguiente diagrama de clases de formas:
public class Shape {
int id;
Color color;
Thickness thickness;
//other fields
}
public class Rectangle : Shape {
Point topLeft;
Point bottomRight;
}
public class Circle : Shape {
Point center;
int radius;
}
El diseño de la base de datos para las clases anteriores puede ser así:
table Shape
-----------
int id; (PK)
int color;
int thichkness;
int rowType; (0 = Rectangle, 1 = Circle, 2 = ...)
table Rectangle
----------
int ShapeID; (FK on delete cascade)
int topLeftX;
int topLeftY;
int bottomRightX;
int bottomRightY;
table Circle
----------
int ShapeID; (FK on delete cascade)
int centerX;
int center;
int radius;
Respuesta corta: no lo haces.
Si necesita serializar sus objetos, use un ORM, o incluso algo mejor como activerecord o prevaylence.
Si necesita almacenar datos, almacénelos de manera relacional (teniendo cuidado con lo que está almacenando y prestando atención a lo que Jeffrey L Whitledge acaba de decir), no a uno afectado por el diseño de su objeto.
Tenga en cuenta que algunos motores de base de datos ya proporcionan mecanismos de herencia de forma nativa, como Postgres . Mira la documentation .
Por ejemplo, consultaría el sistema Persona / Empleado descrito en una respuesta anterior como esta:
/* This shows the first name of all persons or employees */ SELECT firstname FROM Person ; /* This shows the start date of all employees only */ SELECT startdate FROM Employee ;
¡En esa es la elección de su base de datos, no necesita ser particularmente inteligente!
Usando SQL ALchemy (Python ORM), puede hacer dos tipos de herencia.
La experiencia que he tenido es usar una tabla singe y tener una columna discriminante. Por ejemplo, una base de datos de ovejas (¡no es broma!) Almacenó todas las ovejas en la misma tabla, y las carnadas y las ovejas se manejaron usando una columna de género en esa tabla.
Por lo tanto, puedes consultar todas las Ovejas y obtener todas las Ovejas. O puede consultar solo por Ram, y solo obtendrá Rams. También puede hacer cosas como tener una relación que solo puede ser un Ram (es decir, el padre de una oveja), y así sucesivamente.
repetir respuesta de hilo similar
en el mapeo OR, la herencia se correlaciona con una tabla padre donde las tablas padre e hijo usan el mismo identificador
por ejemplo
create table Object (
Id int NOT NULL --primary key, auto-increment
Name varchar(32)
)
create table SubObject (
Id int NOT NULL --primary key and also foreign key to Object
Description varchar(32)
)
SubObject tiene una relación de clave externa a Object. cuando crea una fila SubObject, primero debe crear una fila de Objeto y usar Id en ambas filas
EDITAR: si también está buscando modelar el comportamiento, necesitaría una tabla de Tipo que enlistara las relaciones de herencia entre las tablas, y especificara el ensamble y el nombre de la clase que implementó el comportamiento de cada tabla
parece exagerado, ¡pero todo depende de lo que quieras usar!