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, post
e ambas person
devem ser referenciadas (impingidas pela PRIMARY KEY
que cria as duas colunas NOT NULL
automaticamente (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 likes
para post
incluir a author_id
forma redundante. Então você pode excluir o incesto com uma CHECK
restriçã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 UNIQUE
restrição também redundante em post
:
ALTER TABLE post ADD CONSTRAINT post_for_fk_uni UNIQUE (author_id, post_id);
Eu coloquei o author_id
primeiro a fornecer um índice útil enquanto estava nele.
Resposta relacionada com mais:
Mais barato com uma CHECK
restrição
Com base na "Implementação básica" acima.
CHECK
restriçõ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 VALID
para refletir isso adequadamente. Detalhes:
Uma CHECK
restriçã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 IMMUTABLE
funçã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 CHECK
restrição:
ALTER TABLE likes ADD CONSTRAINT no_self_like_chk
CHECK (f_author_id_of_post(post_id) <> person_id) NOT VALID;