Список друзей: дизайн таблиц реляционной базы данных

Итак, современная концепция списка друзей:
Допустим, у нас есть таблица с именем Person. Теперь у этого человека должно быть много друзей (каждый из которых также принадлежит к классу людей). Самый очевидный способ построить отношение — это таблица соединений. то есть

buddyID   person1_id   person2_id
0         1            2
1         3            6

Но когда пользователь хочет увидеть список своих друзей, программа должна будет проверить столбцы «person1_id» и «person2_id», чтобы найти всех своих друзей.

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

buddyID   person1_id   person2_id
0         1            2
1         2            1

Так что нужно искать только один столбец.

Заранее спасибо.


person huntaub    schedule 17.06.2010    source источник


Ответы (2)


Это отношение «многие ко многим», для этого требуется таблица отношений.

create table Person (
   person_id int not null primary key,
   username varchar(100) not null,
   ... other_cols ...
)


create table Buddy (
   person_id1 int not null,
   person_id2 int not null,
   primary key (person_id1, person_id2),
   foreign key (person_id1) reference Person (person_id),
   foreign key (person_id2) reference Person (person_id)
)

Таким образом, таблица Person, очевидно, будет содержать 1 строку для каждого человека. Он будет содержать любые данные о приятелях, поскольку это сделает его денормализованным. Вместо этого таблица друзей будет содержать отношения между людьми.

Допустим, у вас есть что-то вроде этого в таблице Person:

person_id    username
1            George
2            Henry
3            Jody
4            Cara

Генри и Кара — приятели, как и Джордж и Кара, так что:

person_id1   person_id2
2            4
1            4

Если вам нужно, чтобы отношения не были неявно взаимными, вам нужно будет добавить дополнительные строки, чтобы сделать это явным. Итак, теперь давайте предположим, что Генри считает Кару другом, и Кара также думает о Генри как о друге, в то время как Джордж считает Кару другом, но Кара не отвечает Джорджу взаимностью:

person_id1   person_id2
2            4
4            2
1            4

Отсутствие 4 1 указывает на то, что Кара не считает Джорджа другом. Это сохраняет чистоту и позволяет избежать аномалий данных. Вы можете настроить отношения, не искажая данные о человеке. Также вы можете определить правило удаления каскадов для внешних ключей, чтобы удаление Person автоматически удаляло все связанные отношения для вас. И наоборот, вы можете захотеть предотвратить это, вместо этого вы можете указать ограничение (по умолчанию) для внешних ключей, которое предотвратит удаление человека с определенными отношениями.

Запросы тоже просты:

Сколько приятелей у Кары (допустим, отношения для списков приятелей неявны):

select count(*) from Person 
                join Buddy on person_id = person_id1 or person_id = person_id2
 where name = 'Cara'

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

person_id   considers_as_buddy_id
2           4
4           2
1           4
4           3

select count(*) from Person P
                join Buddy B on P.person_id = B.person_id
 where name = 'Cara'

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

select count(*) from Person P
                join Buddy B on P.person_id = B.person_id and 
                                B.considers_as_buddy_id = P.person_id
 where name = 'Cara'
person Khorkrak    schedule 17.06.2010
comment
Спасибо за очень подробный ответ. Оба были очень полезны. - person huntaub; 21.06.2010
comment
Будет ли это пригодным для использования в обычном окне MySql, если у кого-то есть 20 000 приятелей, и он хочет получить весь список за ‹ 1 секунду? - person Luke Belbina; 29.11.2011

Здесь есть несколько возможных сценариев.

Во-первых, вам нужно определить, могут ли дружеские отношения быть односторонними. Может ли человек А быть приятелем человека Б, если Б не является приятелем А? Я бы предположил, что нет, но стоит быть явным.

Здравый смысл подсказывает, что вы запрашиваете больше, чем обновляете отношения. Таким образом, оптимизируйте свои данные для этого. При создании дружеских отношений создайте две записи: от A до B и от B до A. Это сделает запрос тривиальным. Так что да, используйте две записи, как вы уже подумали.

person cletus    schedule 17.06.2010
comment
да. Это имеет большой смысл. Я должен был подумать об этом. Спасибо. - person huntaub; 17.06.2010