¿Cuáles son las mejores prácticas para modelar la herencia en bases de datos?
¿Cuáles son las compensaciones (por ejemplo, consultabilidad)?
(Estoy más interesado en SQL Server y .NET, pero también quiero entender cómo otras plataformas abordan este problema).
.net
sql-server
oop
inheritance
database-design
Incluso Mien
fuente
fuente
Respuestas:
Hay varias formas de modelar la herencia en una base de datos. El que elija depende de sus necesidades. Aquí hay algunas opciones:
Tabla por tipo (TPT)
Cada clase tiene su propia mesa. La clase base tiene todos los elementos de la clase base, y cada clase que se deriva de ella tiene su propia tabla, con una clave primaria que también es una clave foránea para la tabla de la clase base; la clase de la tabla derivada contiene solo los diferentes elementos.
Así por ejemplo:
Resultaría en tablas como:
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 serán dispersas. Se agrega una columna discriminadora que le dice al sistema qué tipo de fila es esta.
Dadas las clases anteriores, terminas con esta tabla:
Para cualquier fila que sea de tipo fila 0 (Persona), la fecha de inicio siempre será nula.
Mesa por concreto (TPC)
Cada clase tiene su propia tabla completamente formada sin referencias a ninguna otra tabla.
Dadas las clases anteriores, terminas con estas tablas:
fuente
El diseño adecuado de la base de datos no se parece en nada al diseño de objeto adecuado.
Si planea utilizar la base de datos para algo que no sea simplemente serializar sus objetos (como informes, consultas, uso de múltiples aplicaciones, inteligencia empresarial, etc.), entonces no recomiendo ningún tipo de mapeo simple de objetos a tablas.
Muchas personas piensan que una fila en una tabla de base de datos es 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 esta comprensión, puede ver que un solo tipo en un programa orientado a objetos puede almacenarse en una docena de relaciones diferentes. Y una variedad de tipos (unidos por herencia, asociación, agregación o completamente no afiliados) pueden almacenarse parcialmente en una sola relación.
Es mejor preguntarse, qué hechos desea almacenar, qué preguntas va a querer respuestas, qué informes desea generar.
Una vez que se crea el diseño adecuado de la base de datos, es simple crear consultas / vistas que le permitan serializar sus objetos a esas relaciones.
Ejemplo:
En un sistema de reserva de hotel, es posible que deba 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 ese 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 desnudo.
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. (Sin embargo, obviamente, los mecanismos internos por los cuales se obtienen esos valores pueden involucrar 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, al intentar almacenar hechos sobre el mundo, en lugar de intentar almacenar entidades con atributos, se construye una base de datos relacional adecuada. Y una vez que se diseña correctamente, las consultas útiles que no se imaginaron durante la fase de diseño se pueden construir fácilmente, ya que todos los hechos necesarios para cumplir con esas consultas se encuentran en sus lugares adecuados.
fuente
Employment
tabla que recopile todos los empleos con sus fechas de inicio. Entonces, si conocer la fecha de inicio de empleo actual de unEmployer
es importante, ese podría ser un caso de uso adecuado para unView
, que incluye esa propiedad mediante consultas. (nota: parece que debido al '-' justo después de mi nick, no recibí ninguna notificación sobre tu comentario)Respuesta corta: no lo haces.
Si necesita serializar sus objetos, use un ORM, o incluso mejor, algo como registro activo o prevalencia.
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 afectado por el diseño de su objeto.
fuente
Los patrones de TPT, TPH y TPC son las formas en que va, como lo menciona Brad Wilson. Pero un par de notas:
Las clases secundarias que heredan de una clase base se pueden ver 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 las ID únicas se almacenan para todas y cada una de las tablas secundarias, al tiempo que se mantiene el FK en la tabla principal. Un FK es suficiente y es aún mejor tener habilitada la cascada de borrado para la relación FK entre el niño y las tablas base.
En TPT, al ver solo los registros de la tabla base, no puede encontrar qué clase secundaria representa el registro. Esto a veces es necesario, cuando desea cargar una lista de todos los registros (sin hacerlo
select
en todas y 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), mezclando el TPT y el TPH de alguna manera.Digamos que queremos diseñar una base de datos que contenga el siguiente diagrama de clase de forma:
El diseño de la base de datos para las clases anteriores puede ser así:
fuente
Hay dos tipos principales de herencia que puede configurar en una base de datos, tabla por entidad y tabla por jerarquía.
Tabla por entidad es donde tiene una tabla de entidad base que tiene propiedades compartidas de todas las clases secundarias. Luego tiene por clase secundaria otra tabla, cada una con solo propiedades aplicables a esa clase. Están vinculados 1: 1 por sus PK
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 posee actualmente
SessionTypeID es discriminador
El objetivo por jerarquía es más rápido de consultar, ya que no necesita uniones (solo el valor discriminador), mientras que el objetivo por entidad necesita hacer uniones complejas para detectar qué tipo es algo y 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 del activo no está completa, por lo tanto, está vacía, pero fue principalmente para mostrar cómo está configurada, no qué poner dentro de las tablas. Eso depende de ti ;). La tabla de sesión contiene información de sesión de colaboración virtual, y puede ser de varios tipos de sesiones, según el tipo de colaboración involucrada.
fuente
Normalizaría su base de datos y eso realmente reflejaría su herencia. Puede tener una degradación del rendimiento, pero así es con la normalización. Probablemente tendrá que usar el buen sentido común para encontrar el equilibrio.
fuente
repetición de respuesta de hilo similar
en la asignación OR, la herencia se asigna a una tabla primaria donde las tablas principal y secundaria usan el mismo identificador
por ejemplo
SubObject tiene una relación de clave externa con Object. cuando crea una fila de SubObjeto, primero debe crear una fila de Objeto y usar el Id en ambas filas
EDITAR: si también está buscando modelar el comportamiento, necesitaría una tabla Tipo que enumerara las relaciones de herencia entre tablas y especificara el ensamblado y el nombre de clase que implementaba el comportamiento de cada tabla
parece excesivo, pero todo depende de para qué lo quieras usar.
fuente
Usando SQL ALchemy (Python ORM), puede hacer dos tipos de herencia.
El que he tenido experiencia 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 una tabla, y Rams y Ewes se manejaron usando una columna de género en esa tabla.
Por lo tanto, puede 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 carnero (es decir, el padre de una oveja), y así sucesivamente.
fuente
Tenga en cuenta que algunos motores de bases de datos ya proporcionan mecanismos de herencia de forma nativa como Postgres . Mira la documentación .
Por ejemplo, consultaría el sistema Persona / Empleado descrito en una respuesta anterior como esta:
En esa es la elección de su base de datos, ¡no necesita ser particularmente inteligente!
fuente