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, post
y ambos person
deben ser referenciados (forzados por el PRIMARY KEY
que hace que ambas columnas NOT NULL
se 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 likes
de post
incluir la author_id
forma redundante. Entonces puede descartar el incesto con una simple CHECK
restricció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 UNIQUE
restricción también redundante en post
:
ALTER TABLE post ADD CONSTRAINT post_for_fk_uni UNIQUE (author_id, post_id);
Lo puse author_id
primero para proporcionar un índice útil mientras lo hago .
Respuesta relacionada con más:
Más barato con una CHECK
restricción
Sobre la base de la "Implementación básica" anterior.
CHECK
las 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 VALID
para reflejar eso adecuadamente. Detalles:
Una CHECK
restricció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 IMMUTABLE
funció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 CHECK
restricción:
ALTER TABLE likes ADD CONSTRAINT no_self_like_chk
CHECK (f_author_id_of_post(post_id) <> person_id) NOT VALID;