Я не бачу, щоб щось тут було циклічним. Між цими сутностями існують люди та посади та два незалежні стосунки. Я б бачив лайки як реалізацію одного з цих відносин.
- Людина може написати багато дописів, допис пише одна людина:
1:n
- Людина може любити багато постів, пост може бути улюбленим багатьма людьми:
n:m
п: відносини м можуть бути реалізовані з іншим співвідношенням: likes.
Основна реалізація
Основна реалізація може виглядати так у 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)
);
Зокрема, зауважте, що у публікації має бути автор ( NOT NULL), хоча наявність лайків необов’язково. Однак для існуючих лайків, postі вони person повинні бути посиланнями (примусові до того, PRIMARY KEYщо обидва стовпці роблять NOT NULLавтоматично (ви можете додати ці обмеження явно, надмірно), тому анонімні лайки також неможливі.
Деталі щодо n: m реалізації:
Запобігайте самолюбству
Ви також писали:
(створена особа не може сподобатися власній посаді).
Це все ще не виконується у зазначеній вище реалізації. Ви можете використовувати тригер .
Або одне з таких швидших / надійніших рішень:
Солідний за вартістю
Якщо він повинен бути непорушними , ви можете розширити FK від likesдо , postщоб включати в себе author_idвеликій кількості. Тоді ви можете виключити інцест з простим CHECKобмеженням.
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)
);
Для цього потрібне інакше також зайве UNIQUEобмеження у post:
ALTER TABLE post ADD CONSTRAINT post_for_fk_uni UNIQUE (author_id, post_id);
Я ставлю author_idперше, щоб поставити корисний індекс , будучи в ньому.
Відповідна відповідь:
Дешевше з CHECKобмеженням
Спираючись на "Основну реалізацію" вище.
CHECKобмеження мають бути незмінні. Посилання на інші таблиці для перевірки ніколи не змінюються, ми тут трохи зловживаємо концепцією. Я пропоную оголосити обмеження, NOT VALIDщоб правильно відобразити це. Деталі:
У CHECKцьому конкретному випадку обмеження здається розумним, оскільки автор публікації здається атрибутом, який ніколи не змінюється. Вимкніть оновлення цього поля, щоб бути впевненим.
Ми підробленаIMMUTABLE функція:
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;
Замініть "public" фактичною схемою ваших таблиць.
Використовуйте цю функцію з CHECKобмеженням:
ALTER TABLE likes ADD CONSTRAINT no_self_like_chk
CHECK (f_author_id_of_post(post_id) <> person_id) NOT VALID;