Não vejo nada sendo cíclico aqui. Existem pessoas e cargos e dois relacionamentos independentes entre essas entidades. Eu veria gostos como a implementação de um desses relacionamentos.
- Uma pessoa pode escrever muitas postagens, uma postagem é escrita por uma pessoa:
1:n
- Uma pessoa pode gostar de muitos posts, um post pode ser apreciado por muitas pessoas:
n:m
O n: relacionamento m pode ser implementado com outra relação: likes.
Implementação básica
A implementação básica pode ser assim no 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)
);
Observe, em particular, que uma postagem deve ter um autor ( NOT NULL), enquanto a existência de curtidas é opcional. No entanto, para curtidas existentes, poste ambas person devem ser referenciadas (impingidas pela PRIMARY KEYque cria as duas colunas NOT NULLautomaticamente (você pode adicionar essas restrições de forma explícita e redundante), para que curtidas anônimas também sejam impossíveis.
Detalhes para a implementação n: m:
Evitar auto-como
Você também escreveu:
(a pessoa criada não pode gostar de sua própria postagem).
Isso ainda não é imposto na implementação acima. Você poderia usar um gatilho .
Ou uma dessas soluções mais rápidas / confiáveis:
Sólido por um custo
Se ele precisa ser sólida , você pode estender o FK partir likespara postincluir a author_idforma redundante. Então você pode excluir o incesto com uma CHECKrestrição simples .
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)
);
Isso requer uma UNIQUErestrição também redundante em post:
ALTER TABLE post ADD CONSTRAINT post_for_fk_uni UNIQUE (author_id, post_id);
Eu coloquei o author_idprimeiro a fornecer um índice útil enquanto estava nele.
Resposta relacionada com mais:
Mais barato com uma CHECKrestrição
Com base na "Implementação básica" acima.
CHECKrestrições são imutáveis. Fazer referência a outras tabelas para uma verificação nunca é imutável, estamos abusando um pouco do conceito aqui. Sugiro declarar a restrição NOT VALIDpara refletir isso adequadamente. Detalhes:
Uma CHECKrestrição parece razoável nesse caso específico, porque o autor de uma postagem parece um atributo que nunca muda. Não permita atualizações nesse campo para ter certeza.
Nós fingimos uma IMMUTABLEfunção:
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;
Substitua 'public' pelo esquema real de suas tabelas.
Use esta função em uma CHECKrestrição:
ALTER TABLE likes ADD CONSTRAINT no_self_like_chk
CHECK (f_author_id_of_post(post_id) <> person_id) NOT VALID;