Implementación del subtipo de un subtipo en el patrón de diseño de tipo / subtipo con subclases mutuamente excluyentes

20

Introducción

Para que esta pregunta sea útil para futuros lectores, utilizaré el modelo de datos genéricos para ilustrar el problema que enfrento.

Nuestro modelo de datos consta de 3 entidades, que se etiquetarán como A, By C. Para mantener las cosas simples, todos sus atributos serán de inttipo.

Entidad Atiene los siguientes atributos: D, Ey X;

Entidad Btiene los siguientes atributos: D, Ey Y;

La entidad Ctiene los siguientes atributos: Dy Z;

Como todas las entidades comparten atributos comunes D, he decidido aplicar el diseño de tipo / subtipo .

Importante: ¡Las entidades son mutuamente excluyentes! Esto significa que la entidad es A, B o C.

Problema:

Las entidades Ay Btienen otro atributo común E, pero este atributo no está presente en la entidad C.

Pregunta:

Me gustaría utilizar la característica descrita anteriormente para optimizar aún más mi diseño, si es posible.

Para ser honesto, no tengo idea de cómo hacer esto, ni dónde comenzar a intentarlo, de ahí esta publicación.

Siempre aprendiendoNuevo
fuente

Respuestas:

6

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 Ea la tabla de tipo base
  • manteniéndolo en múltiples tablas de subtipo
  • normalizando completamente Ea una nueva tabla de subclases intermedias en el mismo nivel que Ceso Ay que Bserán directamente subclases de ( respuesta de @ MDCCL )

Veamos cada opción:

Mover propiedad Ea 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 EcomoNOT NULL
  • Necesita más CHECK CONSTRAINTen la tabla de tipo base para asegurarse de que E IS NULLcuando EntityType = C(aunque esto no es un gran problema)
  • Es necesario educar a los usuarios del modelo de datos sobre por qué Edebe ser NULL, e incluso debe ignorarse por completo, cuando EntityType = C.
  • Ligeramente menos eficiente cuando Ees un tipo de longitud fija, y una gran parte de las filas son para EntityType de C(es decir, no se utiliza, Epor lo tanto, es NULL), y no se utiliza la SPARSEopción en la columna o la Compresión de datos en el índice agrupado
  • Potencialmente menos eficiente para consultas que no necesitan, Eya que la presencia Een 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 Een 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 Een 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 NULLsi fuera una propiedad requerida de la entidad
  • No es necesario un extra CHECK CONSTRAINTen la tabla de tipo base para garantizar que E IS NULLcuando 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+ Btenga en comparación con cuántas filas de Callí hay.
  • Ligeramente más difícil / complejo para operaciones que tratan únicamente con entidades Ay B(y no C ) como el mismo "tipo". Por supuesto, puede abstraer esto a través de una Vista que hace una UNION ALLentre una SELECTde las tablas UNIDAS para Ay otra SELECTde las tablas UNIDAS para B. Esto reducirá la complejidad de las consultas SELECT, pero no tan útil para los INSERTy UPDATElas 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 Ea 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 Ay B, pero no C(es decir, no se necesitan dos consultas unidas mediante UNION ALL)

Contras

  • un poco más de espacio ocupado (la Bartabla 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 JOINque se necesita para llegar a cualquiera AoB
  • mayor área de superficie para el bloqueo, principalmente activada INSERT( DELETEse 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 - Ao B- dentro de la Tabla de clase base Foo,; solo conoce el tipo Brque puede ser Ao 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 JOINla Barmesa Esto también reducirá la efectividad de indexar la FooTypeCodecolumna.

  • No hay un enfoque consistente para interactuar con A& Bvs 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 Ea 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 SubTypeIDvalor) o combinar dos o más subtipos en uno. Por el contrario, ¿qué Epasaría si más tarde se convirtiera en una propiedad común de todos los subtipos? Entonces la capa intermedia de la Bartabla 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 Bartabla 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 Epropiedad es compartida entre los tipos de entidad Ay Bno significa que Ay 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 Ey con qué frecuencia se ejecutarán
  • qué consultas necesitará que no necesite Ey con qué frecuencia se ejecutarán

Creo que tiendo por defecto a mantener Elas tablas de subtipo separadas porque, como mínimo, es "más limpio". Consideraría pasar Ea 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 Ey / 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 Ea la tabla de intermediario entre la base y la clase Ay 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 Ea cabo a una tabla de intermediario entre la mesa de la clase base y el Ay Bsubclase mesas podría funcionar (aunque eso todavía le deja sin conocimiento directo del tipo específico ( AoB) 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 :-).

Solomon Rutzky
fuente
17

Según Martin Fowler, hay 3 enfoques para el problema de la herencia de tablas:

  • Herencia de tabla única : una tabla representa todos los tipos. Los atributos no utilizados están NULADOS.
  • Herencia de tabla de concreto : una tabla por tipo de concreto, cada columna de tabla para cada atributo del tipo. No hay relación entre tablas.
  • Herencia de tabla de clase : una tabla por tipo, cada tabla tiene atributos solo para atributos nuevos, no heredados. Las tablas están relacionadas y reflejan la jerarquía de herencia de tipo real.

Puede comenzar con estos como un punto de partida para buscar los pros y los contras de cada enfoque. La esencia de esto es que todos los enfoques tienen grandes desventajas, y ninguno tiene una ventaja abrumadora. Mejor conocido como el desajuste de impedancia relacional del objeto , este problema aún no ha encontrado una solución.

Personalmente, encuentro que el tipo de problemas a los que puede conducir un mal diseño relacional son órdenes de magnitud más graves que el tipo de problemas que surgen de un mal diseño de tipo . El mal diseño de la base de datos conduce a consultas lentas, anomalías de actualización, explosión del tamaño de datos, puntos muertos y aplicaciones que no responden, y decenas a cientos de Gigabytes de datos hundidos en el formato incorrecto . El diseño de tipo incorrecto lleva a que sea difícil mantener y actualizar el código , no el tiempo de ejecución. Por lo tanto, en mi libro, el diseño relacional correcto prevalece sobre cualquier pureza tipo OO una y otra vez.

Remus Rusanu
fuente
@AlwaysLearningNewStuff Creo que esta pregunta es un seguimiento en dba.stackexchange.com/questions/139092 , ¿correcto? En la puesta en práctica allí hacer que la herencia de tablas.
Remus Rusanu
Sí, antes de hacer esta pregunta, quería asegurarme de haber entendido correctamente cómo implementar primero el diseño de tipo / subtipo. Ahora me enfrento al problema descrito anteriormente cuando algunas subclases (¡pero no todas!) Tienen atributos compartidos. Me preguntaba si hay algo que pueda hacer para optimizar el modelo de datos en ese caso, en lugar de ignorar ese matiz ...
AlwaysLearningNewStuff
6

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 Fooy Bar, 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 ):

Figura 1 - Modelo de datos de relaciones hipotéticas de supertipo-subtipo

La adición de Foo y Bar

No agregué Fooy Barpara que el modelo se vea mejor, sino para hacerlo más expresivo. Considero que son importantes debido a lo siguiente:

  • Como Ay Bcomparte el atributo nombrado E, 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 del Bartipo de superentidad que, a su vez, es Un tipo de subentidad Foo, que mantiene el Datributo en la parte superior de la jerarquía.

  • Como Csolo comparte un atributo con el resto de los tipos de entidad en discusión, es decir, Deste 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 El Footipo 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:

  • Cada aparición de tipo de superentidad exclusiva está relacionada con un solo complemento de tipo de subentidad .

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:

CREATE TABLE FooType -- Look-up table.
(
    FooTypeCode     CHAR(2)  NOT NULL,
    Description     CHAR(90) NOT NULL, 
    CreatedDateTime DATETIME NOT NULL,
    CONSTRAINT PK_FooType             PRIMARY KEY (FooTypeCode),
    CONSTRAINT AK_FooType_Description UNIQUE      (Description)
);

CREATE TABLE Foo -- Supertype
(
    FooId           INT      NOT NULL, -- This PK migrates (1) to ‘Bar’ as ‘BarId’, (2) to ‘A’ as ‘AId’, (3) to ‘B’ as ‘BId’, and (4) to ‘C’ as ‘CId’.
    FooTypeCode     CHAR(2)  NOT NULL, -- Discriminator column.
    D               INT      NOT NULL, -- Column that applies to ‘Bar’ (and therefore to ‘A’ and ‘B’) and ‘C’.
    CreatedDateTime DATETIME NOT NULL,
    CONSTRAINT PK_Foo                 PRIMARY KEY (FooId),
    CONSTRAINT FK_from_Foo_to_FooType FOREIGN KEY (FooTypeCode)
        REFERENCES FooType (FooTypeCode)
);

CREATE TABLE BarType -- Look-up table.
(
    BarTypeCode CHAR(1)  NOT NULL,  
    Description CHAR(90) NOT NULL,  
    CONSTRAINT PK_BarType             PRIMARY KEY (BarTypeCode),
    CONSTRAINT AK_BarType_Description UNIQUE      (Description)
);

CREATE TABLE Bar -- Subtype of ‘Foo’.
(
    BarId       INT     NOT NULL, -- PK and FK.
    BarTypeCode CHAR(1) NOT NULL, -- Discriminator column. 
    E           INT     NOT NULL, -- Column that applies to ‘A’ and ‘B’.
    CONSTRAINT PK_Bar             PRIMARY KEY (BarId),
    CONSTRAINT FK_from_Bar_to_Foo FOREIGN KEY (BarId)
        REFERENCES Foo (FooId),
    CONSTRAINT FK_from_Bar_to_BarType FOREIGN KEY (BarTypeCode)
        REFERENCES BarType (BarTypeCode)    
);

CREATE TABLE A -- Subtype of ‘Bar’.
(
    AId INT NOT NULL, -- PK and FK.
    X   INT NOT NULL, -- Particular column.  
    CONSTRAINT PK_A             PRIMARY KEY (AId),
    CONSTRAINT FK_from_A_to_Bar FOREIGN KEY (AId)
        REFERENCES Bar (BarId)  
);

CREATE TABLE B -- (1) Subtype of ‘Bar’ and (2) supertype of ‘A’ and ‘B’.
(
    BId INT NOT NULL, -- PK and FK.
    Y   INT NOT NULL, -- Particular column.  
    CONSTRAINT PK_B             PRIMARY KEY (BId),
    CONSTRAINT FK_from_B_to_Bar FOREIGN KEY (BId)
        REFERENCES Bar (BarId)  
);

CREATE TABLE C -- Subtype of ‘Foo’.
(
    CId INT NOT NULL, -- PK and FK.
    Z   INT NOT NULL, -- Particular column.  
    CONSTRAINT PK_C             PRIMARY KEY (CId),
    CONSTRAINT FK_from_C_to_Foo FOREIGN KEY (FooId)
        REFERENCES Foo (FooId)  
);

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 TRANSACTIONSpara 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:


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.

MDCCL
fuente
¡Mis empleadores cambiaron la lógica de negocios, eliminándolos por Ecompleto! 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!
AlwaysLearningNewStuff