Это отношение многие ко многим, требуется таблица отношений.
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:
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 автоматически удаляло все связанные с вами отношения. И наоборот, вы можете захотеть предотвратить это, вместо этого в этом случае вы можете указать ограничение (по умолчанию) для внешних ключей, что предотвратит удаление Person с еще определенными отношениями.
Запросы тоже просты:
Сколько друзей имеет Cara (допустим, что отношения для списков друзей неявны):
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'