En la medida en que esta pregunta es una continuación de ¿Es correcta mi implementación del patrón de diseño de tipo / subtipo (para subclases mutuamente excluyentes)? , que en sí mismo es una continuación de No sé cómo transformar una entidad variable en una tabla relacional , yo preguntaría: ¿ qué es exactamente lo que estás tratando de optimizar? ¿Almacenamiento? El modelo de objeto? ¿Complejidad de consulta? Consulta de rendimiento? Hay compensaciones al optimizar un aspecto frente a otro, ya que no puede optimizar todos los aspectos al mismo tiempo.
Estoy completamente de acuerdo con los puntos de Remus con respecto a:
- Hay ventajas y desventajas de cada enfoque (es decir, el factor "depende" siempre presente), y
- La primera prioridad es la eficiencia del modelo de datos (un modelo de datos ineficiente no se puede corregir con un código de aplicación limpio y / o eficiente)
Dicho esto, la elección que enfrenta es entre las siguientes, organizadas en orden de menor normalización a mayor normalización:
- promoción de propiedad
E
a la tabla de tipo base
- manteniéndolo en múltiples tablas de subtipo
- normalizando completamente
E
a una nueva tabla de subclases intermedias en el mismo nivel que C
eso A
y que B
serán directamente subclases de ( respuesta de @ MDCCL )
Veamos cada opción:
Mover propiedad E
a la tabla de tipo base
PRO
- Complejidad reducida consulta para las consultas que necesitan
E
, pero no X
, Y
, o Z
.
- Potencialmente más eficiente para las consultas que necesitan
E
, pero no X
, Y
, o Z
(especialmente consultas agregadas) debido a no unirse.
- Potencial para crear índice en
(D, E)
(y si es así, potencialmente un índice filtrado en (D, E)
donde EntityType <> C
, si tal condición está permitida)
Contras
- No se puede marcar
E
comoNOT NULL
- Necesita más
CHECK CONSTRAINT
en la tabla de tipo base para asegurarse de que E IS NULL
cuando EntityType = C
(aunque esto no es un gran problema)
- Es necesario educar a los usuarios del modelo de datos sobre por qué
E
debe ser NULL
, e incluso debe ignorarse por completo, cuando EntityType = C
.
- Ligeramente menos eficiente cuando
E
es un tipo de longitud fija, y una gran parte de las filas son para EntityType de C
(es decir, no se utiliza, E
por lo tanto, es NULL
), y no se utiliza la SPARSE
opción en la columna o la Compresión de datos en el índice agrupado
- Potencialmente menos eficiente para consultas que no necesitan,
E
ya que la presencia E
en la tabla de tipo base aumentará el tamaño de cada fila, lo que a su vez disminuye el número de filas que pueden caber en una página de datos. Pero esto depende en gran medida del tipo de datos exacto de E
, FILLFACTOR, cuántas filas hay en la tabla de tipo base, etc.
Mantener la propiedad E
en cada tabla de subtipo
PRO
- Modelo de datos más limpio (es decir, no tiene que preocuparse por educar a otros sobre por qué la columna
E
en la tabla de tipo base no debe usarse porque "realmente no está allí")
- Probablemente se parece más al modelo de objetos.
- Puede marcar la columna como
NOT NULL
si fuera una propiedad requerida de la entidad
- No es necesario un extra
CHECK CONSTRAINT
en la tabla de tipo base para garantizar que E IS NULL
cuando EntityType = C
(aunque esto no sea una gran ganancia)
Contras
- Requiere JOIN para subtipar tablas para obtener esta propiedad
- Potencialmente un poco menos eficiente cuando se necesita
E
, debido a JOIN, dependiendo de cuántas filas de A
+ B
tenga en comparación con cuántas filas de C
allí hay.
- Ligeramente más difícil / complejo para operaciones que tratan únicamente con entidades
A
y B
(y no C
) como el mismo "tipo". Por supuesto, puede abstraer esto a través de una Vista que hace una UNION ALL
entre una SELECT
de las tablas UNIDAS para A
y otra SELECT
de las tablas UNIDAS para B
. Esto reducirá la complejidad de las consultas SELECT, pero no tan útil para los INSERT
y UPDATE
las consultas.
- Dependiendo de las consultas específicas y de la frecuencia con la que se ejecutan, esto podría ser una ineficiencia potencial en los casos en que tener un índice
(D, E)
realmente ayudaría a una o más consultas de uso frecuente, ya que no pueden indexarse juntas.
Normalizar E
a la tabla intermedia entre clase base y A
&B
(Tenga en cuenta que me gusta la respuesta de @ MDCCL como una alternativa viable, dependiendo de las circunstancias. Lo siguiente no pretende ser una crítica estricta de ese enfoque, sino un medio para agregar alguna perspectiva, la mía, por supuesto, al evaluar en el mismo contexto que las dos opciones que ya había propuesto. Esto hará que sea más fácil aclarar lo que veo como la diferencia relativa entre la normalización completa y el enfoque actual de la normalización parcial).
PRO
- el modelo de datos está completamente normalizado (no puede haber nada inherentemente incorrecto con esto, dado que es para lo que están diseñados los RDBMS)
- complejidad de consulta reducida para consultas que necesitan
A
y B
, pero no C
(es decir, no se necesitan dos consultas unidas mediante UNION ALL
)
Contras
- un poco más de espacio ocupado (la
Bar
tabla duplica la ID y hay una nueva columna BarTypeCode
) [insignificante, pero algo a tener en cuenta]
- ligero aumento en la complejidad de consulta como un adicional
JOIN
que se necesita para llegar a cualquiera A
oB
- mayor área de superficie para el bloqueo, principalmente activada
INSERT
( DELETE
se puede manejar implícitamente marcando claves externas como ON CASCADE DELETE
) ya que la transacción se mantendrá abierta un poco más en la tabla de la clase base (es decir Foo
) [insignificante, pero algo a tener en cuenta]
sin conocimiento directo del tipo real - A
o B
- dentro de la Tabla de clase base Foo
,; solo conoce el tipo Br
que puede ser A
o B
:
Es decir, si necesita hacer consultas sobre la información base general pero necesita categorizar por tipo de entidad o filtrar uno o más tipos de entidad, entonces la tabla de clase base no tiene suficiente información, en cuyo caso necesita LEFT JOIN
la Bar
mesa Esto también reducirá la efectividad de indexar la FooTypeCode
columna.
No hay un enfoque consistente para interactuar con A
& B
vs C
:
Es decir, si cada entidad se relaciona directamente con la tabla de la clase base de modo que solo haya una UNIÓN para obtener la entidad completa, entonces todos pueden familiarizarse más rápida y fácilmente en términos de trabajar con el modelo de datos. Habrá un enfoque común para las consultas / Procedimientos almacenados que los hará más rápidos de desarrollar y menos propensos a tener errores. Un enfoque consistente también hace que sea más rápido y fácil agregar nuevos subtipos en el futuro.
potencialmente menos adaptable a las reglas comerciales que cambian con el tiempo:
Es decir, las cosas siempre cambian, y es bastante fácil pasar E
a la tabla de clase base si se vuelve común a todos los subtipos. También es bastante fácil mover una propiedad común a los subtipos si los cambios en la naturaleza de las entidades hacen que sea un cambio que valga la pena. Es bastante fácil dividir un subtipo en dos subtipos (simplemente cree otro SubTypeID
valor) o combinar dos o más subtipos en uno. Por el contrario, ¿qué E
pasaría si más tarde se convirtiera en una propiedad común de todos los subtipos? Entonces la capa intermedia de la Bar
tabla no tendría sentido, y la complejidad añadida no valdría la pena. Por supuesto, es imposible saber si tal cambio ocurriría en 5 o incluso 10 años, por lo que la Bar
tabla no es necesariamente, ni muy probable que sea, una mala idea (por eso dije " potencialmente menos adaptable"). Estos son solo puntos a considerar; Es una apuesta en cualquier dirección.
agrupamiento potencialmente inapropiado:
Es decir, sólo porque la E
propiedad es compartida entre los tipos de entidad A
y B
no significa que A
y B
debe ser agrupados juntos. El hecho de que las cosas "se vean" iguales (es decir, las mismas propiedades) no significa que sean iguales.
Resumen
Al igual que decidir si / cuándo desnormalizar, la mejor forma de abordar esta situación particular depende de considerar los siguientes aspectos del uso del modelo de datos y asegurarse de que los beneficios superen los costos:
- cuántas filas tendrá para cada EntityType (mire al menos 5 años más adelante, suponiendo un crecimiento superior al promedio)
- ¿Cuántos GB tendrá cada una de estas tablas (tipo base y subtipos) en 5 años?
- qué tipo de datos específico es propiedad
E
- ¿Es solo una propiedad o hay algunas, o incluso varias, propiedades?
- qué consultas necesitará que requieran
E
y con qué frecuencia se ejecutarán
- qué consultas necesitará que no necesite
E
y con qué frecuencia se ejecutarán
Creo que tiendo por defecto a mantener E
las tablas de subtipo separadas porque, como mínimo, es "más limpio". Consideraría pasar E
a la tabla de tipo base IF: la mayoría de las filas no eran para EntityType de C
; y el número de filas era al menos en millones; y las consultas que más a menudo no se ejecutan y que se necesitan E
y / o las consultas que se beneficiarían de un índice se (D, E)
ejecutan con mucha frecuencia y / o requieren suficientes recursos del sistema para que el índice reduzca la utilización general de los recursos, o al menos evite aumentos repentinos en el consumo de recursos que superan los niveles aceptables o duran lo suficiente como para causar un bloqueo excesivo y / o aumentos en los puntos muertos.
ACTUALIZAR
OP comentó sobre esta respuesta que:
¡Mis empleadores cambiaron la lógica de negocios, eliminando E por completo!
Este cambio es particularmente importante porque es exactamente lo que predica que podría ocurrir en la subsección "contras" de la "Normalizar E
a la tabla de intermediario entre la base y la clase A
y B
" anterior (sexto punto). El problema específico es cuán fácil / difícil es refactorizar el modelo de datos cuando ocurren tales cambios (y siempre lo hacen). Algunos argumentarán que cualquier modelo de datos puede ser refactorizado / cambiado, así que comience con el ideal. Pero si bien es cierto a nivel técnico que cualquier cosa puede ser refactorizada, la realidad de la situación es una cuestión de escala.
Los recursos no son infinitos, no solo CPU / Disco / RAM, sino también recursos de desarrollo: tiempo y dinero. Las empresas establecen constantemente prioridades en los proyectos porque esos recursos son muy limitados. Y con bastante frecuencia (al menos en mi experiencia), los proyectos para ganar eficiencia (incluso tanto el rendimiento del sistema como el desarrollo más rápido / menos errores) se priorizan debajo de los proyectos que aumentan la funcionalidad. Si bien es frustrante para nosotros los técnicos porque entendemos cuáles son los beneficios a largo plazo de los proyectos de refactorización, es solo la naturaleza de los negocios que a los técnicos menos técnicos les resulta más fácil ver la relación directa entre la nueva funcionalidad y las nuevas ingresos. Esto se reduce a: "volveremos a arreglar eso más tarde" == "
Con eso en mente, si el tamaño de los datos es lo suficientemente pequeño como para que los cambios se puedan realizar de manera muy consultante, y / o tenga una ventana de mantenimiento que sea lo suficientemente larga como para no solo hacer los cambios sino también revertirlos si algo va mal, entonces la normalización E
a cabo a una tabla de intermediario entre la mesa de la clase base y el A
y B
subclase mesas podría funcionar (aunque eso todavía le deja sin conocimiento directo del tipo específico ( A
oB
) en la tabla de clase base). PERO, si tiene cientos de millones de filas en estas tablas y una increíble cantidad de código que hace referencia a las tablas (código que debe probarse cuando se realizan los cambios), generalmente vale la pena ser más pragmático que idealista. Y este es el entorno con el que tuve que lidiar durante años: 987 millones de filas y 615 GB en la tabla de clase base, repartidas en 18 servidores. Y tanto código golpeó estas tablas (tablas de clase base y subclase) que hubo mucha resistencia, principalmente de la administración, pero a veces del resto del equipo, a realizar cambios debido a la cantidad de desarrollo y Recursos de control de calidad que tendrían que asignarse.
Entonces, una vez más, el "mejor" enfoque solo puede determinarse situación por situación: necesita conocer su sistema (es decir, cuántos datos y cómo se relacionan las tablas y el código), cómo realizar la refactorización y las personas con el que trabaja (su equipo y posiblemente la gerencia, ¿puede obtener su aceptación para tal proyecto?). Hay algunos cambios que había estado mencionando y planeando durante 1 a 2 años, y tomé varios sprints / lanzamientos para lograr que tal vez el 85% de ellos se implementaran. Pero si solo tiene <1 millón de filas y no hay mucho código vinculado a estas tablas, entonces probablemente pueda comenzar en el lado más ideal / "puro" de las cosas.
Solo recuerde, sea cual sea el camino que elija, preste atención a cómo funciona ese modelo durante los próximos 2 años al menos (si es posible). Presta atención a lo que funcionó y a lo que causó dolor, incluso si parecía la mejor idea en ese momento (lo que significa que también debes permitirte aceptar que la cagues, todos lo hacemos) para que puedas evaluar honestamente los puntos de dolor. ) Y preste atención a por qué ciertas decisiones funcionaron o no para que pueda tomar decisiones que tengan más probabilidades de ser "mejores" la próxima vez :-).
Según mi interpretación de sus especificaciones, desea encontrar un método para implementar dos estructuras de subtipo de supertipo diferentes (pero conectadas ) .
Para exponer un enfoque para lograr la tarea mencionada, voy a agregar al escenario en cuestión los dos tipos clásicos de entidades hipotéticas llamadas
Foo
yBar
, que detallaré a continuación.Reglas del negocio
Aquí hay algunas declaraciones que me ayudarán a crear un modelo lógico:
A Foo is either one Bar or one C
A Foo is categorized by one FooType
A Bar is either one A or one C
A Bar is classified by one BarType
Modelo lógico
Y luego, el modelo lógico IDEF1X [1] resultante se muestra en la Figura 1 (y también puede descargarlo de Dropbox como PDF ):
La adición de Foo y Bar
No agregué
Foo
yBar
para que el modelo se vea mejor, sino para hacerlo más expresivo. Considero que son importantes debido a lo siguiente:Como
A
yB
comparte el atributo nombradoE
, esta característica sugiere que son tipos de subentidad de un tipo distinto (pero relacionado) de concepto , evento , persona , medida , etc., que representé por medio delBar
tipo de superentidad que, a su vez, es Un tipo de subentidadFoo
, que mantiene elD
atributo en la parte superior de la jerarquía.Como
C
solo comparte un atributo con el resto de los tipos de entidad en discusión, es decir,D
este aspecto insinúa que es un tipo de subentidad de otro tipo de concepto , evento , persona , medida , etc., así que describí esta circunstancia en virtud de ElFoo
tipo de super entidad.Sin embargo, estos son solo supuestos, y dado que una base de datos relacional está destinada a reflejar con precisión la semántica de un determinado contexto comercial , debe identificar y clasificar todas las cosas de interés en su dominio específico para que pueda, precisamente, capturar más significado .
Factores importantes en la fase de diseño.
Es bastante útil tener en cuenta el hecho de que, dejando a un lado toda la terminología, un clúster exclusivo de supertipo-subtipo es una relación ordinaria. Describamos la situación de la siguiente manera:
Por lo tanto, hay una correspondencia (o cardinalidad) de uno a uno (1: 1) en estos casos.
Como sabe por sus publicaciones anteriores, el atributo discriminador (columna, cuando se implementa) desempeña un papel primordial al crear una asociación de esta naturaleza, porque indica la instancia de subtipo correcta con la que está conectado el supertipo . La migración de la CLAVE PRIMARIA de (i) el supertipo a (ii) los subtipos también es de importancia primordial.
Estructura de hormigón DDL
Y luego escribí una estructura DDL que se basa en el modelo lógico presentado anteriormente:
Con esta estructura, evita el almacenamiento de marcas NULL en sus tablas base (o relaciones ), lo que introduciría ambigüedad en su base de datos.
Integridad, consistencia y otras consideraciones.
Una vez que esté implementando su base de datos, debe asegurarse de que (a) cada fila de supertipo exclusiva siempre se complemente con su correspondiente contratipo de subtipo y, a su vez, garantice que (b) dicha fila de subtipo sea compatible con el valor contenido en la columna discriminadora de supertipo . Por lo tanto, es bastante conveniente emplear ACID
TRANSACTIONS
para asegurarse de que se cumplan estas condiciones en su base de datos.No debe renunciar a la solidez lógica, la autoexpresividad y la precisión de su base de datos, estos son aspectos que decididamente hacen que su base de datos sea más sólida.
Las dos respuestas publicadas anteriormente ya incluyen puntos pertinentes que sin duda vale la pena tener en cuenta al diseñar, crear y administrar su base de datos y sus programas de aplicación.
Recuperando datos por medio de definiciones VIEW
Puede configurar algunas vistas que combinen columnas de los diferentes grupos de subtipos de supertipos , de modo que pueda recuperar los datos disponibles sin, por ejemplo, escribir las cláusulas JOIN necesarias cada vez. De esta manera, puede seleccionar directamente desde la vista (una relación derivada o tabla ) de interés con facilidad.
Como puede ver, "Ted" Codd fue, sin duda, un genio. Las herramientas que legó son bastante fuertes y elegantes, y, por supuesto, están bien integradas entre sí.
Recursos Relacionados
Si desea analizar una base de datos extensa que involucra relaciones de supertipo-subtipo, encontrará valiosas las respuestas extraordinarias propuestas por @PerformanceDBA a las siguientes preguntas de desbordamiento de pila:
Base de datos histórica / auditable .
[O] ne table o muchos para muchos eventos diferentes pero interactivos? , que comprende subtipos anidados .
Nota
1. La definición de integración para el modelado de información ( IDEF1X ) es una técnica de modelado de datos muy recomendable que fue establecida como estándar en diciembre de 1993 por el Instituto Nacional de Estándares y Tecnología de los Estados Unidos ( NIST ). Se basa sólidamente en (a) el material teórico inicial escrito por el Dr. EF Codd; en (b) la vista de datos Entidad-Relación , desarrollada por el Dr. PP Chen ; y también en (c) la Técnica de diseño de base de datos lógica, creada por Robert G. Brown. Vale la pena señalar que IDEF1X se formalizó a través de la lógica de primer orden.
fuente
E
completo! La razón para aceptar la respuesta del usuario srutzky es porque proporciona buenos puntos que me ayudan a tomar la decisión de elegir la ruta más eficiente. Si no fuera por eso, aceptaría tu respuesta. He votado tu respuesta antes. ¡Gracias de nuevo!