Как избежать циклической зависимости (циклическая ссылка) между 3 таблицами?

10

У меня есть 3 таблицы:

  • люди
  • После
  • Нравится

Когда я проектирую модель ER, она имеет циклическую зависимость:

         1: N
Люди -------- <Пост

         1: N
Пост ---------- <Нравится

         1: N
Люди -------- <Нравится

Логика такова:

  • 1 человек может иметь много постов.

  • 1 пост имеет много лайков.

  • 1 человеку может нравиться много постов (созданный человек не может понравиться своему посту).

Как я могу удалить этот вид циклического дизайна? Или мой дизайн БД неправильный?

Рагу
источник

Ответы:

10

Бизнес правила

Давайте внесем некоторые изменения в представленные вами бизнес-правила:

  • А Personсоздает ноль-один-или-много Posts .
  • А Postполучает ноль-один-или-много Likes .
  • А Personпроявляет ноль-один-или-много Likes , каждый из которых относится к одному конкретному Post .

Логические модели

Затем из такого набора утверждений я получил две модели данных логического уровня IDEF1X [1] , показанные на рисунке 1 .

Рисунок 1 - Модели данных людей и сообщений

Вариант А

Как вы можете видеть в модели Вариант А, PersonId мигрирует [2] из Personк в Postкачестве внешнего ключа (FK), но он получает имя роли [3] из AuthorId, и этот атрибут составляет, вместе с PostNumberпервичным ключом (PK) типа Postсущности.

Я предполагаю , что Likeможет существовать только в связи с конкретным Post, так что я создал LikeПК , который состоит из трех различных атрибутов: PostAuthorId, PostNumberи LikerId. Комбинация PostAuthorIdи PostNumberявляется FK, который делает правильную ссылку на PostPK. LikerIdэто, в свою очередь, FK, который устанавливает подходящую ассоциацию с Person.PersonId.

С помощью этой структуры вы гарантируете, что определенный человек может продемонстрировать только одно Likeвхождение в один и тот же Postэкземпляр.

Методы, позволяющие запретить автору сообщения свою собственную публикацию

Поскольку вы не хотите допускать, чтобы человеку нравились его / ее авторские посты, на этапе реализации вы должны установить метод, который сравнивает значение Like.PostAuthorIdсо значением Like.LikerIdв каждой попытке INSERT. Если указанные значения совпадают, (a) вы отклоняете вставку, если они не соответствуют (b), вы позволяете процессу продолжаться.

Для выполнения этой задачи в вашей базе данных вы можете использовать:

  1. Проверочное ограничение , но, конечно же , этот метод исключает MySQL, так как она не была реализована в этой платформе до сих пор, как вы можете видеть здесь и здесь .

  2. Строки кода внутри транзакции ACID .

  3. Строки кода в TRIGGER , которые могут возвращать пользовательское сообщение, указывающее на попытку нарушения правила.

Вариант Б

Если автор не является атрибутом, который первичным образом идентифицирует сообщение в вашем бизнес-домене, вы можете использовать структуру, аналогичную той, которая изображена в варианте B.

Этот подход также гарантирует, что сообщение может понравиться одному и тому же человеку только один раз.


Заметки

1. Определение интеграции для информационного моделирования ( IDEF1X ) - это очень рекомендуемый метод моделирования данных, который был определен в качестве стандарта в декабре 1993 года Национальным институтом стандартов и технологий США ( NIST ).

2. IDEF1X определяет миграцию ключей как «процесс моделирования размещения первичного ключа родительского или общего объекта в его дочернем объекте или объекте категории в качестве внешнего ключа».

3. Имя роли - это обозначение, назначенное атрибуту внешнего ключа, чтобы выразить значение такого атрибута в контексте его соответствующего типа объекта. Наименование ролей рекомендуется с 1970 г. д-ром Э. Ф. Коддом в его оригинальной работе под названием «Реляционная модель данных для больших общих банков данных» . Со своей стороны, IDEF1X - сохранение верности в отношении реляционных практик - также поддерживает эту процедуру.

MDCCL
источник
6

Я не вижу здесь ничего цикличного. Есть люди и должности и два независимых отношения между этими объектами. Я бы увидел лайков как реализацию одного из этих отношений.

  • Человек может написать много постов, пост написан одним человеком: 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)
);

Обратите внимание, в частности, что пост должен иметь author ( 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;
Эрвин Брандштеттер
источник
4

Я думаю, что вы испытываете трудности в выяснении этого из-за того, как вы заявляете свои бизнес-правила.

Люди и сообщения являются "объектами". Как глагол.

У вас действительно всего 2 действия:

  1. Человек может создать одну или несколько публикаций
  2. Многим может понравиться много постов. (сборник ваших последних 2 утверждений)

людям нравится пост диаграммы

Таблица «лайков» будет иметь в качестве первичного ключа person_id и post_id.

Николя де Фонтене
источник