Проектирование БД mysql — хранение друзей пользователя
Mysql: выборка всех друзей определенного пользователя
есть таблица friends поля: id sender — Отправитель addressee — Получатель status — Статус.
Хранение друзей в базе данных mysql
Добрый вечер. У меня вопрос. Лучше всего, для каждого пользователя, создавать отдельную таблицу.
Хранение и вывод списка друзей на сервере
Добрый день. Пишу клиент/сервер типа ICQ. Подскажите где лучше хранить данные о друзьях. да и.
Список друзей из вк по id пользователя
Здравствуйте, товарищи. Недавно я заказал разработку своего сайта, функция которого, как мне.
Сообщение от qwertyyyyyw
Спасибо, но я не правильно высказался: как хранить, например, 2 id в строке user_freinds
Добавлено через 16 секунд
Сообщение от lyod
qwertyyyyyw, Хранить несколько значений в одном поле — это не по феншую. Для каждого друга — отдельная запись. Т.е:
user_id | friend_id
1 | 2
1 | 3
1 | 4
1 | 5
2 | 1
2 | 3
Сообщение от Jodah
qwertyyyyyw, Хранить несколько значений в одном поле — это не по феншую. Для каждого друга — отдельная запись. Т.е:
user_id | friend_id
1 | 2
1 | 3
1 | 4
1 | 5
2 | 1
2 | 3
Сообщение от Старец
Сообщение от Старец
Сообщение от Старец
Сообщение от Jodah
ШТА? Ты вообще в теме о чём речь ведёшь? Ты хотя бы раз сравнил скорость выборки и объём БД при таком способе записи?
1) Объём самой таблицы больше раза в четыре при одинаковом количестве данных
2) Время формирования массива в 25 раз выше.
И ты называешь ЭТО по фен-шую?
Сообщение от BarbosLV
ШТА? Ты вообще в теме о чём речь ведёшь? Ты хотя бы раз сравнил скорость выборки и объём БД при таком способе записи? По феншую. ля. начитался "умников" и озвучиваешь глупости.
1) Объём самой таблицы больше раза в четыре при одинаковом количестве данных
2) Время формирования массива в 25 раз выше.
И ты называешь ЭТО по фен-шую?
Сообщение от BarbosLV
Сообщение от BarbosLV
Сообщение от BarbosLV
Возможно предложенный мною способ будет более ресурсозатратным (хотя это требует проверки), но с точки зрения построения БД ваш способ кривой.
Допустим, завтра понадобится добавить рейтинги пользователей (во ВКонтакте он, кстати, есть). Суть: чем чаще вы заходите на страницу вашего друга (или видите его новости, или что-то ещё), тем выше он в списке друзей.
В моём случае задача очень простая:
В вашем — нужно либо выбирать из таблицы данные, преобразовывать, изменять рейтинг и закидывать обратно, либо использовать какой-то хитрый поиск.
Другой пример. Допустим, нам нужно написать функцию, которая при заходе на страницу пользователя выводит 20 его друзей (в ВК тоже имеется). Как это сделать в моём примере? Легко:
В вашем. боюсь представить.
А если понадобится потом ещё пару полей добавить в БД. например, тип друга (одноклассник, коллега и т.д.), уровень доступа к приватным фотоальбомам и т.д., ТС будет рвать на себе волосы от вашего решения.
Сообщение от Jodah
Сообщение от Jodah
Сообщение от Jodah
Сообщение от Jodah
Сообщение от qwertyyyyyw
Сообщение от BarbosLV
Это попытка (бессмысленная) имитировать MongoDB. Jodah описал в общих чертах к чему в будущем может привести такой подход:
https://www.cyberforum.ru/php/. ost7172695
Так же многие нюансы использования подобного подхода можно посмотреть еще тут (на MongoDB в названии статьи не обращайте внимания, подход и проблемы одни и те же):
http://habrahabr.ru/post/231213/
Добавлено через 6 минут
Сообщение от BarbosLV
Помните приложение для телесериалов? Это был идеальный вариант использования для MongoDB. Каждый сериал был одним самодостаточным документом. Нет ссылок на другие документы, нет дублирования, и нет способа сделать данные несогласованными.
После трех месяцев в разработке все прекрасно работало с MongoDB. Но однажды в понедельник на планерке клиент сказал, что один из инвесторов хочет новую фичу. Он хочет иметь возможность кликнуть на на имя актера и посмотреть его карьеру в телесериалах. Он хочет список всех эпизодов во всех сериалах в хронологическом порядке, в которых этот актер снимался.
Мы хранили каждый сериал в виде документа в MongoDB, содержащем все данные, в том числе актеров. Если этот актер встречался в двух эпизодах, даже в одном сериале, информация хранилась в двух местах. Мы не могли даже узнать что это один и тот же актер, кроме как с помощью сравнения имен. Для реализации фичи надо было обойти все документы, найти и дедуплицировать все экземпляры актеров. Ух… Надо было это сделать как минимум один раз, а потом поддерживать внешний индекс всех актеров, который будет испытывать те же проблемы с согласованностью, как и любой другой кеш.
Видите что происходит?
Клиент ожидает что фича будет тривиальной. Если бы данные были в реляционном хранилище, то это было бы действительно так. В первую очередь мы попытались убедить менеджера что эта фича не нужна. Но менеджер не прогнулся и мы придумали несколько более дешевых альтернатив, вроде ссылок на IMDB по имени актера. Но компания делала деньги на рекламе, поэтому им нужно было чтобы клиент не уходил с сайта.
Эта фича подтолкнула проект к переходу на PosgreSQL. После общения с заказчиком выяснилось, что бизнес видит много ценности в связывании эпизодов между собой. Они предполагали просмотр сериалов, снятых одним режиссером и эпизодов, выпущенных в одну неделю и многое другое.
Это было в конечном счете, проблема коммуникации, а не техническая проблема. Если эти разговоры, что произошли раньше, если бы мы взяли время, чтобы действительно понять, как клиент хочет видеть данные и что хочет делать с ним, то мы, вероятно, перешли бы PostgreSQL ранее, когда было меньше данных, и было легче.
Как реализовать хранение друзей в БД?
Как хранить связи вида пользователь-друг — понятно. Создаем таблицу friend в которой два столбца — user и friend. Делаем ключ по двум полям. Соответственно, пользователь А, добавил в друзья пользователя B — появилась соответствующая запись. Когда пользователь В подтвердил заявку в друзья — создается симметричная запись.
Далее, если хотим вывести друзей пользователя, пишем что-то вроде
select f1.friend from friends f1 join friends f2 on f1.user=f2.friend and f2.user=f1.friend where f1.user=:user_id
Но что делать если мы хотим вывести не только т.н «взаимных друзей» но и заявки в друзья и не подтвержденные заявки в друзья.
т.е например:
Пользователь user1 добавил в друзья пользователей user2, user3. user2 подтвердил заявку. user4 добавил в друзья user1. И в профиле user1 должно выводиться:
user2 (удалить из друзей)
user3 (отозвать заявку)
user4 (принять заявку)
Как правильно написать запрос? Или одним запросом не обойтись?
- Вопрос задан более трёх лет назад
- 10387 просмотров
- Вконтакте
А что если хранить немного по другому?
Например не создавать дублирующую запись в обратную сторону, а изначально использовать еще одно поле в троичной системе счисления: ± 1 когда один пользователь добавил другого (знак указывает направление заявки) и 0 когда заявка подтверждена.
Тогда запрос будет один на выборку пары, а состояния отозвать/принять заявку и удалить из друзей будут определяться знаком числа в дополнительном поле.
Из очевидных плюсов. Места занимать будет примерно в два раза меньше — мелочь, а приятно.
Минусов не сразу не соображу.
- Вконтакте
Просто тогда непонятно как выбирать друзей пользователя. Если делать что-то вроде select * from friend where user = :user_id or friend = :user_id, то не совсем понятно становится, кто кого добавил friend юзера в друзья или юзер френда. Ну и соответственно запись вида
будет означать одно и то же. Понятно, что добавляться будет только одна из них, но мороки, имхо, слишком много
Записи не идентичны.
если 1 добавил 2, то будет запись 1 2 0
если 2 добавил 1, то будет запись 2 1 0
Тут по записи видно кто инициировал добавление, кроме того, можно даже для дополнительного поля использовать bool, характеризующий подтвержденность дружбы. А направление запроса брать из полей user и friend.
Все что выше — мое видение направления решения. Идти в этом направлении или нет — решать вам.
- Вконтакте
Так хранить друзей это не очень хорошая идея. Если на сайте будет 1000 пользователей и у каждого по 100 друзей, то у вас будет таблица на 200000 записей, и довольно медленные запросы для такой простой штуки как список друзей.
Я бы сделал денормализацию, то есть просто хранил бы список друзей строкой в поле модели user 🙂
Быстрые запросы, так как пропарсить строку в несколько сотен символов будет быстрее, чем делать запрос к таблице в несколько сотен тысяч записей, да и в разработке такой способ проще.
Хотя возможно я ошибаюсь и разница в производительности будет не такой большой.
Собственно, отвечаю на ваш вопрос. Нужно просто получить список всех записей, где user1 есть в поле user или friend, затем уже в коде определить, взаимные это друзья (есть запись где user1 в поле friend, и запись где user1 в поле user, второй пользователь одинаковый в обоих запросах) или кто-то из них только отправил запрос. Запрос будет чем-то вроде:
select user, friend from friends where user=:user_id or friend=:user_id
- Вконтакте
> у вас будет таблица на 200000 записей
И что? И 50 лямов — не проблема для тупого индекс-скана даже на убитой виртуалке.
А вот хранить массив в поле таблицы — ветвь тупиковая. Чуть только понадобится найти «кто добавил в друзья меня», а не кого добавил этот пользователь — сразу получается full-scan, который быстро выполняться не может в принципе.
теоретически вам надо два множества — друзья юзера, и обратное — те кто позвал в друзья вашего юзера.
пересечение будет давать взаимных френдов, вычитания — невзимных с одной и с другой стороны.
синтаксис SQL говорит что это легко можно сделать полным join, который обычно не имплементируется но эмулируется обьединением left и right. То бишь:
и будет 3 варианта — обе f1 и f2 не null — взаимные друзья или один из них null
Но это не самый эффективный способ, я бы согласился с serso и посоветовал иметь доп колонку с типом ( заодно можно их иметь несколько — друзья, супруги/любовники, коллеги ) и при добавлении проверять обратное отношение и сразу заполнять колонку. На нагруженной системе это можно делать в отложенном режиме.
Создайте таблицу друзей для сайта социальной сети
Привет, я столкнулся с проблемой, касающейся структуры таблицы для хранения списка всех друзей пользователя. В приведенной ниже таблице, которую я использовал, если пользователь 84 запросил дружбу с пользователем 70 & amp; 74 , то пользователь 84 является друзьями: 70 & amp; 74 и наоборот. Аналогично пользователь 77 с 84 .
Но проблема в том, что если я хочу узнать всех друзей и их детали пользователя 84 , то есть пользователя ( 70,74,77 ).
Следует ли вводить повторяющуюся запись, такую как изображение 2 Пример: если пользователь 84 отправляет запрос дружбы пользователю 70 , когда пользователь 70 принимает запрос, то другая строка будет вставлять как user_id 70 & amp; friend_id 84 .
ИЛИ есть ли какой-либо запрос mysql для присоединения к таблице пользователей на основе значения столбца таблицы друзей. Например, если user_id равен 84 , он будет присоединяться на основе friend_id , а если friend_id равно 84 , он присоединится к таблице пользователей с помощью столбец user_id .
7 ответов
Я предполагаю, что в вашей системе дружба симметрична, то есть, если A дружит с B, это означает, что B также дружит с A. В этом случае я предлагаю вам изменить столбцы таблицы Friend, чтобы отразить тот факт, что два человека имеют равные статус в отношениях, например
- @ User1_Id
- @ User2_Id
Имея это на месте, вы можете сделать запрос примерно так
При этом используется подвыбор, а не объединение, плюс объединение для получения единого списка идентификаторов пользователей друзей в зависимости от того, каким образом пользователи перечислены в таблице «Друзья». Документация по подзапросу и union.
Вероятно, стоит сохранить это так, а не упрощать код, имея 2 строки в таблице друзей для каждого отношения — (A, B) и (B, A). Если у вас есть N пользователей, таблица Friends будет иметь до N * (N-1) строк, если вы дублируете, и половину, если вы этого не сделаете.
После долгих поисков я нашел лучший способ решить проблему, используя операцию соединения, как показано ниже, а также без ввода повторяющихся записей. Вот мой запрос
Я сделал user_id = 84 статическим, чтобы сделать это динамическим в соответствии с требованиями.
Я не пробовал, но надеюсь, что у вас сработает.
Этот запрос предоставит вам все записи, которые включают друзей пользователя XXXX.
Основываясь на дизайне первой таблицы, вы можете использовать UNION для этого, чтобы получить всех друзей для определенного идентификатора пользователя:
Вы можете использовать запрос соединения, чтобы получить результат. Нет необходимости добавлять повторяющиеся записи
Вместо этого я предлагаю использовать приведенную ниже структуру для решения этой проблемы. Создайте две разные таблицы. А именно 1- Frindship 2- FreindshipStatus
В таблице дружбы, когда userId1 отправляет запрос на добавление в друзья к userId2, тогда recoed вставляется со статусом 1. Поскольку userId2 принимает запрос на добавление в друзья, статус становится 2. Если userid2 Отменен запрос, статус становится 3. И даже если вы хотите получить данные. Предположим, вы хотите узнать, кто является другом с идентификатором пользователя 3. Затем используйте такой запрос:
Как лучше всего хранить данные о "друзьях" пользователя в базе данных?
Столкнулся с проблемой. Как лучше всего записывать «друзей» пользователя. Сначала я думал, что буду записывать так.
Но когда понял, что когда на сайте будет хотя бы 5000 пользователей — база будет огромной.
Подскажите, как лучше всего хранить это в базе?
30 млрд -у фейсбук стока не будет.
Такая таблица имеет смысл существования, но у неё есть свои недостатки.
1 из которых. Вот именно в данном примере мы видим что у пользователя 1 есть 3 друга.
допустим запрос будет выглядеть так
Получим список друзей пользователя 1. Вроде все бы ничего получили список. Но встает вопрос вот все друзья пользователя 1 приняли его в друзья.
тогда запрос вида
Должен возвратить список друзей пользователя 2, он это сделает, но сделает в том случае если только пользователь 2 первый предложил дружбу, тогда будет запись
т.е. пользователю 2 не вылезет инфа что он дружит с пользователем 1.
Это самый существенный косяк.
Но его можно избежать так. Допустим пользователь 1 подал заявку на пользователя 2. создалась запись
Когда пользователь 2 подтвердил заявку в друзья — создается симметричная запись.
Но это увеличение БД ровно в 2 раза, что не есть хорошо.
Решение и тут есть, на мой взгляд.
Добавить 3е поле.
link — в данном случае имеет значение 0 — т.е. пользователь 2 принял заявку в друзья. 1 — не принял. И сразу видно кто предложил дружбу, в данном случае пользователь 2.
Это уменьшит кол-во записей в таблице. Но запрос на выборку друзей тогда будет немного другой.
Но тут опять же тупик в плане вытаскиваются 2 поля, и нужно проверять их значения, что для последующей оптимизации вообще не айс.
Есть вариант на мой взгляд простой, но @Mr Trololo Вы ему за ответ поставили минусы. Но он в чем-то прав товарищи.
Рассмотрим пример. Есть у нас таблица
В чем фишка, мы храним в поле не просто строку 2,4,55,123 а массив ID пользователей, у каждого он свой, на каждого пользователя 1 запись в БД, расти такая таблица будет несомненно но уже меньше чем предыдущий вариант. В чем прикол массива
- Получить список пользователей просто
- Получить список общих друзей тоже. Как? array_intersect возвращает общие значения из нескольких массивов.
- Добавить/удалить/изменить значение в массиве достаточно просто.
- В такой записи можно указывать и отношение связей, добавлять при этом доп поле не нужно, все в массиве. Как? Допустим есть значение 23. Оно положительное, значит примем за правило, если в массиве ID пользователя записан положительно, значит он предложил дружбу, если отрицательное, то ему предложили. array(«-23″,»2″,»-5″,»6″); двоих пригласил он двое пригласили его.
- Как-то нужно учитывать принятие в друзья!? Да, не вопрос сделайте в этом же массиве перед ID пользователя array(«?23″,»2″,»?5″,»?6″); знак ? это значит что решение о дружбе еще не принято. Ну или другой знак, символ.+ есть 2 варианта вы добавляете и вас добавляют, если вы добавляете тогда ставьте ? один знак, если вас добавляют ?? два знака
- Да при таком раскладе есть дублирование информации, но вытащить её в последствии проще.
- Хранить её можно в сериализованном массиве, в JSON кому как удобнее. Но в данном случае именно массив это чтобы работала array_intersect она есть в php.
- Найти кто добавил в друзья меня
select user-id-two from friends where user-id-one=UID // получим список всех пользователей Далее из массива вычленить отрицательные значения. Да скажите что это геморрой, но в данном случае вы же можете этот запрос закешировать! И данные у вас всегда под рукой!
- Тоже самое и с веткой кого я добавил в друзья
- Ну и конечно же те кто еще не подтвердили заявку от Вас, и кому вы не подтвердили заявку.
Примерное описание такой. Давайте кидайте помидоры! Только свежие, я их люблю 🙂
В общем сначала скушайте эту информацию, думаю это один из правильных вариантов решения вопроса о дружбах на сайтах. Это один из вариантов решения, он не претендует на правильность, т.к. только что написал всю эту многабукф сюда. Это просто представление решения проблемы