Подтвердить что ты не робот

Дизайн базы данных: лучшая структура таблицы для захвата отношений "Пользователь/Друг"?

Я пытаюсь создать модель данных, которая означает, что один пользователь является другом другого пользователя. Это то, к чему я придумал, но кажется неуклюжим, есть ли лучшее решение?

User
=====
Id
Name
etc...

UserFriend
===========
UserId
FriendId
IsMutual
IsBlocked
4b9b3361

Ответ 1

UserRelationship
====
RelatingUserID
RelatedUserID
Type[friend, block, etc]

Согласитесь, что взаимность не входит в число столбцов; нарушает нормализацию.

Ответ 2

Чтобы перейти на одну запись на двух пользователей и избежать использования дополнительной памяти, предлагаемой предлагаемыми методами (в два раза больше, чем нужно, поскольку для каждого пользователя есть две записи), вы можете сделать следующее:

  • Структура таблицы:

    USER_RELATIONSHIP {
        user_first_id,
        user_second_id,
        type
    
        primary key(user_first_id, user_second_id)
    }
    
  • Убедитесь, что user_first_id < user_second_id

  • Самая интересная часть - type: для всех возможных состояний отношения вы создаете соответствующие значения. Для exmaple:

    pending_first_second
    pending_second_first
    friends
    block_first_second
    block_second_first
    block_both
    

Что у вас есть:

  • user_first_id < user_second_id гарантирует, что есть только один запись отношения между двумя данными пользователями, поскольку это и ограничения первичного ключа не позволит помещать его иначе.
  • При правильном переключении между состояниями отношений вы определяете, как каждый из двух пользователей относится друг к другу. Если между двумя пользователями нет связи, нет записи.
  • Чтобы узнать взаимосвязь между двумя пользователями (а также обновление), вы просто переходите к одному запросу:

    select * from USER_RELATIONSHIP where
        user_first_id = user1_id and
        user_second_id = user2_id;
    

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

Пример сценария:

  • no record: не находятся в отношениях

  • pending_first_second: первый сделал запрос друга на второй

  • friends: второй одобрил запрос друга

  • no record: один из пользователей удалил другого из своих firends

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

Ответ 3

В настоящее время я создаю сайт социальной сети для клиента, и я выразил это таким образом

CREATE TABLE [dbo].[PersonFriend] (
    [Id]                          INT            IDENTITY (1, 1) NOT NULL,
    [Timestamp]                   DATETIME       NOT NULL,
    [ChangeUser]                  NVARCHAR (200) NOT NULL,
    [FriendStatusId]              TINYINT        NOT NULL,
    [Person1Id]                   INT            NOT NULL,
    [Person2Id]                   INT            NOT NULL,
    [Person1RequestTimestamp]     DATETIME       NOT NULL,
    [Person2AcknowledgeTimestamp] DATETIME       NULL
);

Каждый человек хранится в таблице Person (представьте, что). Поля Person1Id и Person2Id являются FK для таблицы лиц. Я сохраняю список статусов в таблице FriendStatus для проверки того, было ли что-то запрошено, принято, отклонено, проигнорировано и т.д. Поле Timestamp является стандартным в моем дизайне, чтобы указать создание записи (это элемент шаблона, который используется базовым классом персистентности ) и его тип дублируется в этой таблице, так как Person1RequestTimestamp содержит те же данные. Я также фиксирую, когда Person2 увидел запрос и сделал действие (которое указывается в FriendStatusId) на нем и сохранит его в Person2AcknowledgeTimestamp).

Одно из основных предположений этой конструкции может быть указано, что Person1 запросил дружбу Person2 - если эта дружба будет принята, то дружба считается взаимной.

Ответ 4

Я бы сделал что-то похожее на то, что у вас есть, но удалите флаг "IsMutual". Просто добавьте вторую строку с обратными значениями, когда она взаимна. Он добавляет строки, но чувствует себя намного чище.

Ответ 5

Возможно, добавьте таблицу отношений, разместите там свойства отношений и ссылайтесь на нее с UserFriend.

Ответ 6

Вероятно, сверху, но можно использовать семантический веб для моделирования этого. Можно использовать FOAF (FOAF Friend of the Friend) -формат.

Ответ 7

Что вы думаете о моем решении?

У вас есть 3 таблицы

1 **the user** (id,.etc)
2 **friend_request**(id,flaggerID,flaggedID)
3 **friend**(id,frienderID,friendedID)

Вы входите в систему, проверяете, есть ли я в таблице друзей, если у меня есть список друзей (я в дружбе > список дружелюбный) (я в дружбе > listfriender)

Есть ли у меня запрос? (Я в flaggedID?) Знаете? Если нет, удалите запись; если да, создайте новую запись в запросе, так как меня запишут в флагман, и флаггер попадет под флагом. Теперь у нас есть взаимность между 2 записями в таблице запроса, поэтому мы удаляем их и помещаем их в таблицу друзей. Легко отделить req/friends.

Ответ 8

Я сделал это вот так:

Пользователь TABLE

id  name
-----------
1   foo
2   roo
3   shoo
4   mooo

Отношение таблицы TABLE

id   u_id f_id
--------------
1    1    2
2    2    1
3    3    1
4    1    3

Каждый запрос друга принимается вставить обратный способ 1 2 & 2 1 и простой запрос:

$ufq=mysql_query("SELECT t1.f_id,t2.id,t2.name FROM friends AS t1, user AS t2 WHERE t1.u_id='$u_id' AND t2.id=t1.f_id ORDER BY t2.name ")or die(mysql_error());
while($fn=mysql_fetch_array($ufq)){
    echo $fn["name"].'<br>';
}

Ответ 9

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

Ответ 10

Вам действительно нужна физическая таблица, чтобы узнать, есть ли общие друзья? Почему бы не выполнить SQL-запрос, например:

SELECT U.ID, U.NAME FROM USER U
INNER JOIN USERFRIEND UF
ON U.ID = UF.FRIENDID
WHERE U.ID = (SELECT USER.ID FROM USER
            WHERE USER.ID = friend_id AND USER.ID != your_id);

Результаты запроса должны возвращать всех общих друзей.

Ответ 11

Я думаю, вам нужно создать две таблицы:

1. пользователь
     u_id int
     Строка u_username
     balahhh............

2. Дружба
     fs_id int
     related_id int
     related_id int

Ответ 12

Хорошо, я опаздываю на вечеринку, но вот моя ставка.

Сначала таблицы:

User
-id

Type
-id

Relationship
-user_id_1
-user_id_2
-type_id

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

friend
ignored

Это упрощает добавление новых типов. Мне не нужно думать и создавать все возможные комбинации всех моих типов. Я просто добавляю новый тип.

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

Выполнение запросов очень просто. Вот как вы получаете всех друзей в MySQL:

SELECT u.* FROM user u
LEFT JOIN relationship r1 ON u.id = r1.user_id_2
LEFT JOIN relationship r2 ON u.id = r2.user_id_1
WHERE r1.user_id_1 = <my_user_id> # my relationship with them
AND r1.type_id = <friend_type_id> # i say i'm a friend
AND r2.user_id_2 = <my_user_id> # their relationship with me
AND r2.type_id = <friend_type_id> # they say they're friends

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

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

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