У меня есть таблица людей, которых я хочу связать друг с другом, многие-ко-многим, с двунаправленными ссылками

представьте, что вы живете в очень упрощенном примере земли - и представьте, что у вас есть таблица в базе данных MySQL:

create table person (
    person_id int,
    name text
)

select * from person;

+-------------------------------+
|   person_id |            name |
+-------------------------------+
|           1 |           Alice |
|           2 |             Bob |
|           3 |           Carol |
+-------------------------------+

и эти люди должны сотрудничать/работать вместе, так что у вас есть таблица, которая связывает одну запись человека к другому:

create table person__person (
    person__person_id int,
    person_id int,
    other_person_id int
)

Эта настройка означает, что связи между людьми являются однонаправленными-т. е. Алиса может ссылаться на Боба, без Боба, ссылающегося на Алису, и, что еще хуже, Алиса может ссылаться на Боба и Боб может ссылка на Алису одновременно, в двух отдельных записях ссылок. Поскольку эти связи представляют собой рабочие отношения,в реальном мире все они являются двусторонними отношениями. В этой настройке возможно следующее:

select * from person__person;

+---------------------+-----------+--------------------+
|   person__person_id | person_id |    other_person_id |
+---------------------+-----------+--------------------+
|                   1 |         1 |                  2 |
|                   2 |         2 |                  1 |
|                   3 |         2 |                  2 |
|                   4 |         3 |                  1 |
+---------------------+-----------+--------------------+

например, с person_ _ person _ id = 4 выше, когда вы просматриваете профиль Кэрол (person_id = 3), Вы должны увидеть отношения с Алисой (person_id = 1), и когда вы просматриваете профиль Алисы, вы должны увидеть отношения с Кэрол, даже если ссылка идет в обратном направлении.

Я понимаю, что я могу сделать объединение и различные запросы и что не представить отношения как взаимные в пользовательском интерфейсе, но есть ли лучший способ? У меня такое чувство, что там и лучший способ, когда эта проблема аккуратно растает, правильно настроив базу данных, но я ее не вижу. У кого-нибудь есть идея получше?

4 ответов


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

С вашей таблицы отношений can указывают на однонаправленные отношения, я бы предложил рассматривать их как таковые. Другими словами, для каждого отношения, я бы добавил две строки. Если Алиса сотрудничает с Бобом, таблица должна быть следующей:

select * from person__person;
+---------------------+-----------+--------------------+
|   person__person_id | person_id |    other_person_id |
+---------------------+-----------+--------------------+
|                   1 |         1 |                  2 |
|                   2 |         2 |                  1 |
+---------------------+-----------+--------------------+

причина в том, что во многих ActiveRecord (Rails) как и системы, объект таблицы "многие ко многим" недостаточно умен, чтобы запросить person_id и other_person_id. Сохраняя две строки, объекты ActiveRecord like будут работать правильно.

то, что вы должны сделать, это обеспечить целостность ваших данных на уровне кода. Каждый раз, когда устанавливается связь между двумя пользователями, необходимо вставить две записи. При уничтожении связи обе записи должны быть удалены. Пользователи не должны устанавливайте отношения с самими собой.


Я не вижу способа использовать простые реляционные концепции. Вам нужно будет добавить" бизнес-код", чтобы обеспечить соблюдение ваших личных отношений.

  • одним из способов может быть применение второй записи отношений с помощью триггера вставки
  • затем объявите одну из записей "первичной" (например, ту, где person_id меньше, чем другой _person_id)
  • затем создайте представления и склейте код для своих приложений (выберите, обновите, удалите), которые имеют доступ данные с этим знанием.

нет лучшей идеи. Реляционные базы данных не могут выполнить то, что вы просите, поэтому вам нужно написать специальный запрос для получения данных и триггер для выполнения ограничения.

чтобы получить связанных лиц для @person я бы пошел на:

SELECT CASE person_id WHEN @person 
           THEN other_person_id 
          ELSE person_id
       END as related_person_id  
FROM person_person 
WHERE (  person_id=@person 
      OR other_person_id=@person)

вы должны найти этот пост полезным:

http://discuss.joelonsoftware.com/default.asp?design.4.361252.31

Как @ user опубликовал, вам обычно лучше создавать две записи на двунаправленные отношения (Алиса к Бобу и Боб к Алисе в вашем примере). Это делает запрос много проще и точнее отражает отношения. Если у вас есть истинные однонаправленные отношения, это единственный способ летать.