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;