Diseño de base de datos: normalización de una relación "(muchos a muchos) a muchos"


13

Version corta

Tengo que agregar un número fijo de propiedades adicionales a cada par en una unión de muchos a muchos existente. Saltando a los diagramas a continuación, ¿cuál de las Opciones 1-4 es la mejor manera, en términos de ventajas y desventajas, de lograr esto extendiendo el Caso Base? ¿O hay una alternativa mejor que no haya considerado aquí?

Versión más larga

Actualmente tengo dos tablas en una relación de muchos a muchos, a través de una tabla de unión intermedia. Ahora necesito agregar enlaces adicionales a propiedades que pertenecen al par de objetos existentes. Tengo un número fijo de estas propiedades para cada par, aunque una entrada en la tabla de propiedades puede aplicarse a varios pares (o incluso puede usarse varias veces para un par). Estoy tratando de determinar la mejor manera de hacer esto, y estoy teniendo problemas para resolver cómo pensar en la situación. Semánticamente parece que puedo describirlo como cualquiera de los siguientes igualmente bien:

  1. Un par vinculado a un conjunto de un número fijo de propiedades adicionales
  2. Un par vinculado a muchas propiedades adicionales
  3. Muchos (dos) objetos vinculados a un conjunto de propiedades
  4. Muchos objetos vinculados a muchas propiedades.

Ejemplo

Tengo dos tipos de objetos, X e Y, cada uno con ID únicos, y una tabla de enlace objx_objycon columnas x_idy y_id, que juntas forman la clave principal para el enlace. Cada X se puede relacionar con muchas Y, y viceversa. Esta es la configuración de mi relación existente de muchos a muchos.

Caso base

Caso base

Ahora, además, tengo un conjunto de propiedades definidas en otra tabla, y un conjunto de condiciones bajo las cuales un par dado (X, Y) debe tener la propiedad P. El número de condiciones es fijo, y lo mismo para todos los pares. Básicamente dicen "En la situación C1, el par (X1, Y1) tiene la propiedad P1", "En la situación C2, el par (X1, Y1) tiene la propiedad P2", y así sucesivamente, para tres situaciones / condiciones para cada par en la unión mesa.

Opción 1

En mi situación actual hay exactamente tres de estas condiciones, y no tengo ninguna razón para esperar que para aumentar, por lo que una posibilidad es añadir columnas c1_p_id, c2_p_idy c3_p_idque featx_featy, especificando para un determinado x_idy y_id, que la propiedad p_idde uso en cada uno de los tres casos .

Opción 1

Esto no me parece una gran idea, porque complica el SQL para seleccionar todas las propiedades aplicadas a una característica, y no escala fácilmente a más condiciones. Sin embargo, impone el requisito de un cierto número de condiciones por par (X, Y). De hecho, es la única opción aquí que lo hace.

opcion 2

Cree una tabla de condiciones condy agregue la ID de condición a la clave principal de la tabla de unión.

opcion 2

Una desventaja de esto es que no especifica el número de condiciones para cada par. Otra es que cuando solo estoy considerando la relación inicial, con algo como

SELECT objx.*, objy.* FROM objx
  INNER JOIN objx_objy ON objx_objy.x_id = objx.id
  INNER JOIN objy ON objy.id = objx_objy.y_id

Luego tengo que agregar una DISTINCTcláusula para evitar entradas duplicadas. Esto parece haber perdido el hecho de que cada par debería existir solo una vez.

Opción 3

Cree una nueva 'ID de par' en la tabla de unión, y luego tenga una segunda tabla de enlaces entre la primera y las propiedades y condiciones.

Opción 3

Esto parece tener la menor cantidad de desventajas, además de la falta de imposición de un número fijo de condiciones para cada par. Sin embargo, ¿tiene sentido crear una nueva ID que identifique nada más que las ID existentes?

Opción 4 (3b)

Básicamente lo mismo que la Opción 3, pero sin la creación del campo ID adicional. Esto se logra poniendo ambas identificaciones originales en la nueva tabla de unión, por lo que contiene x_idy y_idcampos, en lugar de xy_id.

Opcion 4

Una ventaja adicional de este formulario es que no altera las tablas existentes (aunque todavía no están en producción). Sin embargo, básicamente duplica una tabla completa varias veces (o se siente así, de todos modos), por lo que tampoco parece ideal.

Resumen

Mi sensación es que las opciones 3 y 4 son lo suficientemente similares como para que yo pueda elegir cualquiera de ellas. Probablemente ya lo hubiera hecho si no fuera por el requisito de un número pequeño y fijo de enlaces a propiedades, lo que hace que la Opción 1 parezca más razonable de lo que sería de otra manera. Según algunas pruebas muy limitadas, agregar una DISTINCTcláusula a mis consultas no parece afectar el rendimiento en esta situación, pero no estoy seguro de que la Opción 2 represente la situación tan bien como a las demás, debido a la duplicación inherente causada por la colocación los mismos pares (X, Y) en varias filas de la tabla de enlaces.

¿Es una de estas opciones mi mejor camino a seguir, o hay otra estructura que debería considerar?


En general, 1 y 4 parecen las mejores opciones, estoy de acuerdo. No sería fácil aplicar el número fijo (3) de propiedades con la opción 4, pero creo que es factible.
ypercubeᵀᴹ

Para la DISTINCTcláusula, que estaba pensando en una consulta como la que al final del # 2, que une xy ya través de xyc, pero no se refiere a c... Así que si he (x_id, y_id, c_id)limitado UNIQUEcon filas (1,1,1)y (1,1,2), a continuación SELECT x.id, y.id FROM x JOIN xyc JOIN y, voy a volver dos idénticos filas (1,1), y (1,1).
Michael Underwood

1
Ah ok Descartaría la opción 2 de todos modos. Iría con 1 o 4.
ypercubeᵀᴹ

Cuanto más lo pienso, más siento que limitar el número de propiedades a exactamente tres es el menos importante de mis requisitos. Entonces, salvo comentarios constructivos adicionales en el próximo rato, probablemente iré con el # 4 en este punto. ¡Gracias por tu aporte, @ypercube!
Michael Underwood

Respuestas:


7
  • Opción 1

    * Esto no me parece una gran idea, porque complica el SQL para seleccionar todas las propiedades aplicadas a una característica ...

    No necesariamente complica la consulta SQL (vea la conclusión a continuación).

    ... y no se adapta fácilmente a más condiciones ...

    Se escala fácilmente a más condiciones, siempre que haya un número fijo de condiciones y no haya docenas o cientos.

    Sin embargo, impone el requisito de un cierto número de condiciones por par (X, Y). De hecho, es la única opción aquí que lo hace. *

    Lo hace, y aunque usted dice en un comentario que este es "el menos importante de mis requisitos", no ha dicho que no importa en absoluto.

  • opcion 2

    Una desventaja de esto es que no especifica el número de condiciones para cada par. Otra es que cuando solo estoy considerando la relación inicial ... luego tengo que agregar una cláusula DISTINCT para evitar entradas duplicadas ...

    Creo que puede descartar esta opción debido a las complicaciones que menciona. La objx_objytabla es probable que sea la mesa de conducción para algunos de sus consultas (por ejemplo, "seleccionar todas las propiedades aplicadas a una función", que estoy tomando el sentido de todas las propiedades aplicadas a una objxo objy). Puede usar una vista para preaplicar, por DISTINCTlo que no es una cuestión de complicar las consultas, pero eso va a escalar muy mal en términos de rendimiento para muy poca ganancia.

  • Opción 3

    Sin embargo, ¿tiene sentido crear una nueva ID que identifique nada más que las ID existentes?

    No, no lo hace: la opción 4 es mejor en todos los aspectos.

  • Opcion 4

    ... básicamente duplica una tabla completa varias veces (o se siente así, de todos modos), por lo que tampoco parece ideal.

    Esta opción está bien: es la forma obvia de establecer las relaciones si el número de propiedades es variable o está sujeto a cambios

Conclusión

Mi preferencia sería la opción 1 si objx_objyes probable que el número de propiedades sea ​​estable, y si no puede imaginar agregar más que un puñado extra. También es la única opción que impone la restricción 'número de propiedades = 3'; aplicar una restricción similar en la opción 4 probablemente implicaría agregar c1_p_id... columnas a la tabla xy de todos modos *.

Si realmente no le importa mucho esa condición, y también tiene razones para dudar de que la condición del número de propiedades será estable, elija la opción 4.

Si no está seguro de cuál, elija la opción 1: es más simple y definitivamente es mejor si tiene la opción, como han dicho otros. Si se pospone la opción 1 "... porque complica el SQL para seleccionar todas las propiedades aplicadas a una característica ..." Sugiero crear una vista para proporcionar los mismos datos que la tabla adicional en la opción 4:

Opción 1 tablas:

create table prop(id integer primary key);
create table objx(id integer primary key);
create table objy(id integer primary key);

create table objx_objy(
  x_id integer references objx
, y_id integer references objy
, c1_p_id integer not null references prop
, c2_p_id integer not null references prop
, c3_p_id integer not null references prop
, primary key (x_id, y_id)
);

insert into prop(id) select generate_series(90,99);
insert into objx(id) select generate_series(10,12);
insert into objy(id) select generate_series(20,22);

insert into objx_objy(x_id,y_id,c1_p_id,c2_p_id,c3_p_id)
select objx.id, objy.id, 90, 91, 90+floor(random()*10)
from objx cross join objy;

ver para 'emular' la opción 4:

create view objx_objy_prop as
select x_id
     , y_id
     , unnest(array[1,2,3]) c_id
     , unnest(array[c1_p_id,c2_p_id,c3_p_id]) p_id
from objx_objy;

"seleccionar todas las propiedades aplicadas a una característica":

select distinct p_id from objx_objy_prop where x_id=10 order by p_id;

/*
|p_id|
|---:|
|  90|
|  91|
|  97|
|  98|
*/

dbfiddle aquí


-3

Creo que cualquiera de estas opciones podría funcionar, pero elegiría la opción 1 si el número de condiciones es realmente fijo en 3, y la opción 2 si no lo es. La navaja de Occam también funciona para el diseño de bases de datos, siendo todos los demás factores iguales, el diseño más simple suele ser el mejor.

Aunque si desea seguir reglas estrictas de normalización de la base de datos, creo que necesitaría ir con 2 independientemente de si el número de condiciones es fijo.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.