Estoy pensando en cómo representar una estructura compleja en una base de datos de SQL Server.
Considere una aplicación que necesita almacenar detalles de una familia de objetos, que comparten algunos atributos, pero tienen muchos otros que no son comunes. Por ejemplo, un paquete de seguro comercial puede incluir cobertura de responsabilidad, motor, propiedad e indemnización dentro del mismo registro de póliza.
Es trivial implementar esto en C #, etc., ya que puede crear una Política con una colección de Secciones, donde la Sección se hereda según sea necesario para los diversos tipos de cobertura. Sin embargo, las bases de datos relacionales no parecen permitir esto fácilmente.
Puedo ver que hay dos opciones principales:
Cree una tabla de políticas, luego una tabla de secciones, con todos los campos requeridos, para todas las variaciones posibles, la mayoría de las cuales serían nulas.
Cree una tabla de políticas y numerosas tablas de secciones, una para cada tipo de cobertura.
Ambas alternativas parecen insatisfactorias, especialmente porque es necesario escribir consultas en todas las Secciones, lo que implicaría numerosas uniones o numerosas comprobaciones nulas.
¿Cuál es la mejor práctica para este escenario?
Respuestas:
@Bill Karwin describe tres modelos de herencia en su libro SQL Antipatterns , al proponer soluciones al antipatrón SQL Entity-Attribute-Value . Este es un breve resumen:
Herencia de tabla única (también conocida como Herencia de tabla por jerarquía):
Usar una sola tabla como en su primera opción es probablemente el diseño más simple. Como mencionó, muchos atributos que son específicos de subtipo tendrán que recibir un
NULL
valor en las filas donde estos atributos no se aplican. Con este modelo, tendría una tabla de políticas, que se vería así:Mantener el diseño simple es una ventaja, pero los principales problemas con este enfoque son los siguientes:
Cuando se trata de agregar nuevos subtipos, tendría que modificar la tabla para acomodar los atributos que describen estos nuevos objetos. Esto puede volverse problemático rápidamente cuando tiene muchos subtipos, o si planea agregar subtipos de manera regular.
La base de datos no podrá imponer qué atributos se aplican y cuáles no, ya que no hay metadatos para definir qué atributos pertenecen a qué subtipos.
Tampoco puede aplicar los
NOT NULL
atributos de un subtipo que deberían ser obligatorios. Tendría que manejar esto en su aplicación, que en general no es ideal.Herencia de mesa de hormigón:
Otro enfoque para abordar la herencia es crear una nueva tabla para cada subtipo, repitiendo todos los atributos comunes en cada tabla. Por ejemplo:
Este diseño básicamente resolverá los problemas identificados para el método de tabla única:
Los atributos obligatorios ahora se pueden hacer cumplir
NOT NULL
.Agregar un nuevo subtipo requiere agregar una nueva tabla en lugar de agregar columnas a una existente.
Tampoco hay riesgo de que se establezca un atributo inapropiado para un subtipo particular, como el
vehicle_reg_no
campo para una política de propiedad.No es necesario el
type
atributo como en el método de tabla única. El tipo ahora está definido por los metadatos: el nombre de la tabla.Sin embargo, este modelo también tiene algunas desventajas:
Los atributos comunes se mezclan con los atributos específicos del subtipo, y no hay una manera fácil de identificarlos. La base de datos tampoco lo sabrá.
Al definir las tablas, tendría que repetir los atributos comunes para cada tabla de subtipo. Eso definitivamente no es SECO .
La búsqueda de todas las políticas independientemente del subtipo se vuelve difícil y requeriría un montón de
UNION
s.Así es como tendría que consultar todas las políticas independientemente del tipo:
Observe cómo agregar nuevos subtipos requeriría que la consulta anterior se modifique con un adicional
UNION ALL
para cada subtipo. Esto puede conducir fácilmente a errores en su aplicación si se olvida esta operación.Herencia de tabla de clase (también conocida como Herencia de tabla por tipo):
Esta es la solución que @David menciona en la otra respuesta . Crea una sola tabla para su clase base, que incluye todos los atributos comunes. Luego crearía tablas específicas para cada subtipo, cuya clave principal también sirve como clave externa para la tabla base. Ejemplo:
Esta solución resuelve los problemas identificados en los otros dos diseños:
Los atributos obligatorios se pueden hacer cumplir
NOT NULL
.Agregar un nuevo subtipo requiere agregar una nueva tabla en lugar de agregar columnas a una existente.
No hay riesgo de que se establezca un atributo inapropiado para un subtipo particular.
No es necesario el
type
atributo.Ahora los atributos comunes ya no se mezclan con los atributos específicos de subtipo.
Podemos quedarnos SECOS, finalmente. No es necesario repetir los atributos comunes para cada tabla de subtipo al crear las tablas.
Administrar un incremento automático
id
para las políticas se vuelve más fácil, porque esto puede ser manejado por la tabla base, en lugar de que cada tabla de subtipo las genere de forma independiente.La búsqueda de todas las políticas, independientemente del subtipo, ahora se vuelve muy fácil: no
UNION
es necesario, solo aSELECT * FROM policies
.Considero que el enfoque de la tabla de clase es el más adecuado en la mayoría de las situaciones.
Los nombres de estos tres modelos provienen del libro Patterns of Enterprise Application Architecture de Martin Fowler .
fuente
La tercera opción es crear una tabla "Política", luego una tabla "SectionsMain" que almacena todos los campos que son comunes en los tipos de secciones. Luego, cree otras tablas para cada tipo de sección que solo contengan los campos que no son comunes.
Decidir cuál es mejor depende principalmente de cuántos campos tiene y cómo desea escribir su SQL. Todos trabajarían. Si solo tiene unos pocos campos, entonces probablemente iría con el n. ° 1. Con "muchos" campos, me inclinaría hacia el # 2 o # 3.
fuente
Con la información proporcionada, modelaría la base de datos para que tenga lo siguiente:
Políticas
PASIVO
PROPIEDADES
... y así sucesivamente, porque esperaría que haya diferentes atributos asociados con cada sección de la política. De lo contrario, podría haber una sola
SECTIONS
mesa y, además de lapolicy_id
, habría unasection_type_code
...De cualquier manera, esto le permitiría admitir secciones opcionales por política ...
No entiendo lo que encuentra insatisfactorio sobre este enfoque: así es como almacena los datos mientras mantiene la integridad referencial y no duplica los datos. El término está "normalizado" ...
Debido a que SQL está basado en SET, es bastante ajeno a los conceptos de programación procedimental / OO y requiere un código para la transición de un reino a otro. Los ORM a menudo se consideran, pero no funcionan bien en sistemas complejos de alto volumen.
fuente
Además, en la solución Daniel Vassallo, si usa SQL Server 2016+, hay otra solución que usé en algunos casos sin pérdida considerable de rendimiento.
Puede crear solo una tabla con solo el campo común y agregar una sola columna con el JSON cadena que contiene todos los campos específicos de subtipo.
He probado este diseño para administrar la herencia y estoy muy contento por la flexibilidad que puedo usar en la aplicación relativa.
fuente
La otra forma de hacerlo es usando el
INHERITS
componente. Por ejemplo:Por lo tanto, es posible definir una herencia entre tablas.
fuente
INHERITS
además de PostgreSQL ? MySQL por ejemplo?Me inclino hacia el método n. ° 1 (una tabla de sección unificada), en aras de recuperar de manera eficiente políticas completas con todas sus secciones (que supongo que su sistema hará mucho).
Además, no sé qué versión de SQL Server está usando, pero en 2008+ Columnas dispersas ayudan a optimizar el rendimiento en situaciones donde muchos de los valores en una columna serán NULOS.
En última instancia, tendrá que decidir cuán "similares" son las secciones de política. A menos que difieran sustancialmente, creo que una solución más normalizada podría ser más problemática de lo que vale ... pero solo usted puede hacer esa llamada. :)
fuente
Alternativamente, considere usar bases de datos de documentos (como MongoDB) que admiten de forma nativa estructuras de datos enriquecidas y anidamiento.
fuente
Mira la respuesta que di aquí
Mapeo uno a uno con fluidez NHibernate con teclas sintéticas
fuente