¿Cómo evitar una dependencia cíclica (referencia circular) entre 3 tablas?

10

Tengo 3 mesas:

  • Personas
  • Enviar
  • Gustos

Cuando diseño el modelo ER tiene una dependencia cíclica:

         1: N
Gente -------- <Publicación

         1: N
Publicar ---------- <Me gusta

         1: N
Gente -------- <Me gusta

La lógica es:

  • 1 personas pueden tener muchas publicaciones.

  • 1 publicación tiene muchos Me gusta.

  • A 1 personas le pueden gustar muchas publicaciones (a la persona creada no le puede gustar su propia publicación).

¿Cómo puedo eliminar este tipo de diseño cíclico? ¿O está mal mi diseño de db?

Ragu
fuente

Respuestas:

10

Reglas del negocio

Permítanos hacer algunas modificaciones a las reglas comerciales que ha presentado:

  • A Personcrea cero uno o muchos Posts .
  • A Postrecibe cero uno o muchos Likes .
  • A se Personmanifiesta cero-uno o muchos Likes , cada uno de los cuales pertenece a uno específico Post .

Modelos lógicos

Luego, a partir de este conjunto de afirmaciones, he derivado los dos modelos de datos de nivel lógico IDEF1X [1] que se muestran en la Figura 1 .

Figura 1 - Modelos de datos de personas y publicaciones

Opcion A

Como puede ver en el modelo de la Opción A, PersonId migra [2] de Persona Postcomo FOREIGN KEY (FK), pero recibe el nombre de rol [3] de AuthorId, y este atributo constituye, junto con PostNumber, PRIMARY KEY (PK) del Posttipo de entidad.

Asumo que Likesólo puede existir en relación con una determinada Post, por lo que han establecido una LikePK que consta de tres atributos diferentes: PostAuthorId, PostNumbery LikerId. La combinación de PostAuthorIdy PostNumberes un FK que hace la referencia adecuada al PostPK. LikerIdes, a su vez, un FK que establece la asociación adecuada con Person.PersonId.

Con la ayuda de esta estructura, se asegura de que una persona determinada solo pueda manifestar una sola Likeocurrencia en la misma Postinstancia.

Métodos para evitar que a un Autor de publicaciones le guste su propia publicación

Como no desea permitir la posibilidad de que a una persona le gusten sus publicaciones creadas, una vez en la fase de implementación, debe establecer un método que compare el valor de Like.PostAuthorIdcon el valor de Like.LikerIden cada intento de INSERTAR. Si dichos valores coinciden, (a) rechaza la inserción, si no coinciden (b) , deja que el proceso continúe.

Para realizar esta tarea en su base de datos, puede hacer uso de:

  1. A CHECK CONSTRAINT pero, por supuesto, este método excluye MySQL, ya que no se ha implementado en esta plataforma hasta ahora, como puede ver aquí y aquí .

  2. Líneas de código dentro de una transacción ACID .

  3. Líneas de código dentro de un DISPARADOR , que podría devolver un mensaje personalizado que indica el intento de violación de la regla.

Opcion B

Si el autor no es un atributo que identifica de manera primaria una publicación en su dominio comercial, podría elegir una estructura similar a la que se muestra en la Opción B.

Este enfoque también asegura que una publicación solo pueda ser del agrado de la misma persona una vez.


Notas

1. La definición de integración para el modelado de información ( IDEF1X ) es una técnica de modelado de datos altamente recomendable que fue definida como un estándar en diciembre de 1993 por el Instituto Nacional de Estándares y Tecnología de los Estados Unidos ( NIST ).

2. IDEF1X define la migración de claves como "El proceso de modelado de colocar la clave primaria de una entidad primaria o genérica en su entidad secundaria o de categoría como una clave externa".

3. Un nombre de rol es una denotación asignada a un atributo de clave externa para expresar el significado de dicho atributo en el contexto de su tipo de entidad correspondiente. El Dr. EF Codd recomienda el nombramiento de roles desde 1970 en su documento seminal titulado "Un modelo relacional de datos para grandes bancos de datos compartidos" . Por su parte, IDEF1X (mantener la fidelidad respecto a las prácticas relacionales) también aboga por este procedimiento.

MDCCL
fuente
6

No veo nada cíclico aquí. Hay personas y publicaciones y dos relaciones independientes entre estas entidades. Vería Me gusta como la implementación de una de estas relaciones.

  • Una persona puede escribir muchas publicaciones, una publicación está escrita por una persona: 1:n
  • Una persona puede recibir muchos mensajes, un puesto puede ser del agrado de muchas personas: n:m
    El n: m relación puede ser implementado con otra relación: likes.

Implementación básica

La implementación básica podría verse así en PostgreSQL :

CREATE TABLE person (
  person_id serial PRIMARY KEY
, person    text NOT NULL
);

CREATE TABLE post (
  post_id   serial PRIMARY KEY
, author_id int NOT NULL  -- cannot be anonymous
     REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE  -- 1:n relationship
, post      text NOT NULL
);

CREATE TABLE likes (  -- n:m relationship
  person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE
, post_id   int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE
, PRIMARY KEY (post_id, person_id)
);

Tenga en cuenta en particular que una publicación debe tener un autor ( NOT NULL), mientras que la existencia de Me gusta es opcional. Sin embargo, para los me gusta existentes, posty ambos person deben ser referenciados (forzados por el PRIMARY KEYque hace que ambas columnas NOT NULLse realicen automáticamente (puede agregar estas restricciones de forma explícita, redundante) por lo que los me gusta anónimos también son imposibles.

Detalles para la implementación n: m:

Prevenir a uno mismo

También escribiste:

(a la persona creada no le puede gustar su propia publicación).

Eso no se aplica en la implementación anterior, todavía. Podrías usar un gatillo .
O una de estas soluciones más rápidas / más confiables:

Sólido como una roca por un costo

Si tiene que ser sólida como una roca , se puede extender el FK partir likesde postincluir la author_idforma redundante. Entonces puede descartar el incesto con una simple CHECKrestricción.

CREATE TABLE likes (
  person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE
, post_id   int 
, author_id int NOT NULL
, CONSTRAINT likes_pkey PRIMARY KEY (post_id, person_id)
, CONSTRAINT likes_post_fkey FOREIGN KEY (author_id, post_id)
     REFERENCES post(author_id, post_id) ON UPDATE CASCADE ON DELETE CASCADE
, CONSTRAINT no_self_like CHECK (person_id <> author_id)
);

Esto requiere una UNIQUErestricción también redundante en post:

ALTER TABLE post ADD CONSTRAINT post_for_fk_uni UNIQUE (author_id, post_id);

Lo puse author_idprimero para proporcionar un índice útil mientras lo hago .

Respuesta relacionada con más:

Más barato con una CHECKrestricción

Sobre la base de la "Implementación básica" anterior.

CHECKlas restricciones están destinadas a ser inmutables. Hacer referencia a otras tablas para un cheque nunca es inmutable, estamos abusando un poco del concepto aquí. Sugiero declarar la restricción NOT VALIDpara reflejar eso adecuadamente. Detalles:

Una CHECKrestricción parece razonable en este caso particular, porque el autor de una publicación parece un atributo que nunca cambia. No permitir actualizaciones a ese campo para estar seguro.

Nos falsa una IMMUTABLEfunción:

CREATE OR REPLACE FUNCTION f_author_id_of_post(_post_id int)
  RETURNS int AS
'SELECT p.author_id FROM public.post p WHERE p.post_id = $1'
LANGUAGE sql IMMUTABLE;

Reemplace 'público' con el esquema real de sus tablas.
Use esta función en una CHECKrestricción:

ALTER TABLE likes ADD CONSTRAINT no_self_like_chk
   CHECK (f_author_id_of_post(post_id) <> person_id) NOT VALID;
Erwin Brandstetter
fuente
4

Creo que está teniendo dificultades para resolver esto debido a cómo establece sus reglas comerciales.

Las personas y las publicaciones son "objetos". Me gusta es un verbo.

Realmente solo tienes 2 acciones:

  1. Una persona puede crear una o más publicaciones
  2. A muchas personas les pueden gustar muchas publicaciones. (una compilación de sus últimas 2 declaraciones)

a la gente le gusta el diagrama de publicaciones

La tabla "Me gusta" tendrá person_id y post_id como clave principal.

Nicolas de Fontenay
fuente