Mysql Query Help для рекомендуемых друзей - PullRequest
0 голосов
/ 05 марта 2012

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

Вот запрос

SELECT 
  IF(Friends.User_Id1 IN (1111,2222),
       Friends.User_Id2,
       Friends.User_Id1) AS 'Friend_Id', 
  CONCAT(User_FirstName," ",User_LastName) AS User_FirstName,
  User_ProfilePic 
FROM Users
JOIN Friends ON
  IF(Friends.User_Id1 IN (1111,2222),
       Friends.User_Id2,
       Friends.User_Id1) = Users.User_Id
WHERE 
 (Friends.User_Id2 IN (1111,2222) OR Friends.User_Id1 IN (1111,2222)) AND 
 (Friends.User_Id2 != MY_ID AND Friends.User_Id1 != MY_ID AND Friends.Status = 1)
LIMIT 10;

В приведенном выше случае, 1111 и 2222 - мои друзья, и я пытаюсь получить всех их друзей, это хорошо, но я хочу:

  1. пользователи уже мои друзья, которые также дружат с 1111 и 2222 и отображаются в списке. Я не хочу их здесь, потому что они уже в другом списке друзей.
  2. Пользователи, которых я заблокировал, т.е. Friends.Status для MY_ID и friends_friend_id = 3, у меня тоже есть один в этом деле, идентификатор пользователя 3333 является другом 2222, и я уже заблокировал его, но он присутствует в списке.

Пожалуйста, сообщите мне, если поиск через IN(1111,2222) также приведет к некоторой проблеме в будущем, потому что количество друзей определенно увеличится. У меня есть список моих друзей через запятую, использующий group_concat перед запросом выше. Все это в хранимой процедуре.

Надеюсь, я четко объяснил проблему.

Ответы [ 2 ]

2 голосов
/ 05 марта 2012

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

Как я понимаю вашу схему, это:

create table Friends (
   user_Id1 int,
   user_Id2 int,
   status int);

Если в любое время существуют отношения с другом, один из идентификаторов находится в позиции 1, а 1 - в позиции 2.

Теперь, предполагая, что мой идентификатор 1212, мой список идентификаторов друга:

 select user_Id1 as my_friends_userId
   from Friends f
   where f.user_Id2 = '1212'
      and status = 1
 union
 select user_Id2 as my_friends_userId
   from Friends f
   where f.user_Id1 = '1212'
     and status = 1;

Список идентификаторов друзей моих друзей:

select f1.user_id1 as friends_of_friends
  from Friends f1
  where f1.user_Id2 in (select user_Id1 as my_friends_userId
      from Friends f
      where f.user_Id2 = '1212'
        and status = 1
      union
      select user_Id2 as my_friends_userId
      from Friends f
      where f.user_Id1 = '1212'
        and status = 1)
union
select user_id2 as friends_of_friends
  from Friends f1
  where f1.user_Id1 in (
      select user_Id1 as my_friends_userId
        from Friends f
        where f.user_Id2 = '1212'
          and status = 1
      union
      select user_Id1 as my_friends_userId
        from Friends f
        where f.user_Id1 = '1212'
          and status = 1);

А затем, чтобы добавить исключения для моих друзей и друзей, которых я заблокировал, это становится:

select f1.user_id1 as friends_of_friends
from Friends f1
where f1.user_Id2 in (select user_Id1 as my_friends_userId  /* sub-query for friends of friends */
      from Friends f
      where f.user_Id2 = '1212'
        and status = 1
      union
      select user_Id2 as my_friends_userId
      from Friends f
      where f.user_Id1 = '1212'
        and status = 1)
and f1.user_id1 not in   /* exclusion of my own friends */
(select user_Id1 as my_friends_userId
      from Friends f
      where f.user_Id2 = '1212'
        and status = 1
      union
      select user_Id2 as my_friends_userId
      from Friends f
      where f.user_Id1 = '1212'
        and status = 1
 )
and f1.user_id1 != '1212'  /* exclusion of myself. */
and f1.user_id1 not in (select user_Id1 as my_friends_userId  /* exlusion of people I've blocked. */
      from Friends f
      where f.user_Id2 = '1212'
        and status = 3
      union
      select user_Id2 as my_friends_userId
      from Friends f
      where f.user_Id1 = '1212'
        and status = 3
 )
union  /* Now do it all over again for user_id2 */
select f2.user_id2 as friends_of_friends
from Friends f2
where f2.user_Id1 in (select user_Id1 as my_friends_userId
      from Friends f
      where f.user_Id2 = '1212'
        and status = 1
      union
      select user_Id2 as my_friends_userId
      from Friends f
      where f.user_Id1 = '1212'
        and status = 1)
and f2.user_id2 not in 
(select user_Id1 as my_friends_userId
      from Friends f
      where f.user_Id2 = '1212'
        and status = 1
      union
      select user_Id2 as my_friends_userId
      from Friends f
      where f.user_Id1 = '1212'
        and status = 1
 )
and f2.user_id2 != '1212'
and f2.user_id2 not in (select user_Id1 as my_friends_userId
      from Friends f
      where f.user_Id2 = '1212'
        and status = 3
      union
      select user_Id2 as my_friends_userId
      from Friends f
      where f.user_Id1 = '1212'
        and status = 3
 )

, где я отметил первый раз для каждого из этих условий. Теперь вы можете видеть беспорядок union, который я должен был сделать для этого. (Что, вероятно, должно быть union distinct)

Вы не должны создавать предложение с group-concat. Несмотря на длину здесь, это быстрее.

Вы можете спросить, что делают отдельные части. Но опять же, мой совет: НЕ ДЕЛАЙТЕ ЭТОГО . Вот почему хороший дизайн стола делает вещи намного проще.

SQL Fiddle для справки и отображения результатов: http://sqlfiddle.com/#!2/e6376/13


РЕДАКТИРОВАТЬ: Просто чтобы добавить, как я бы изменить эту схему.

Неясно, если в вашем приложении отношения между друзьями - это отношения Google (разрешены асимметричные отношения) или Facebook (разрешены только симметричные отношения).

В обоих случаях я бы изменил схему на:

create table Friends (
  individual_userId int,
  friend_userId int,
  status int);

В гугле, дело, все готово. В случае с Facebook я бы использовал эту структуру, но потребовал бы, чтобы для каждого отношения в таблицу помещались две строки. Таким образом, если «1212» - это друзья на Facebook с «0415», то есть (индивидуальный_пользователь, идентификатор_пользователя) строк («1212», «0415») & («0415», «1212»). Чтобы это работало и поддерживалось, потребуются хранимые процедуры для вставок / удалений, чтобы убедиться, что обе строки добавлены и удалены. (Обновления нет - это уникальные идентификаторы.)

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

select f1.friend_userId as friends_of_friends
from Friends f1
where f1.individual_userId in (   /* retrieve my friend list */
      select friend_userId as my_friends_userId
      from Friends f
      where f.individual_userId = '1212'
        and status = 1)
and f1.friend_userId not in (   /* exclusion of my own friends */
      select friend_userId as my_friends_userId
      from Friends f
      where f.individual_userId = '1212'
        and status = 1
 )
and f1.friend_userId not in ( /* exlusion of people I have blocked. */
      select friend_userId as my_friends_userId
      from Friends f
      where f.individual_userId = '1212'
        and status = 3
 )
and f1.friend_userId != '1212'  /* exclusion of myself. */

, с которым гораздо проще иметь дело. Вы также можете переписать это как серию объединений, но я подозреваю, что в качестве первого шага, с помощью таких фраз in и not in легче читать.

Пересмотрено sqlfiddle: http://sqlfiddle.com/#!2/92ff2/1

(Я должен был бы протестировать его с большим набором данных, но моя интуиция говорит, что объединения будут быстрее - но для такого кода я подозреваю, что изучение / получение правильного ответа важнее, чем первоначальная оптимизация для скорость.)

1 голос
/ 05 марта 2012

Как и в ответе Майка, и я тоже, ваша структура таблицы выглядит примерно так:

create table Friends (
   user_Id1 int,
   user_Id2 int,
   status int);

То, что вы, похоже, хотите, - это ОТЛИЧНЫЙ список друзей, которые являются либо вашими друзьями, либо друзьями, НАПРЯМУЮ СВЯЗАННЫМИ с вашими друзьями (то есть: 1 степень разлуки с вами). Итак, давайте перейдем к этому сценарию.

Вы ID 1111 и у вас есть друзья 2222 и 3333.

У человека 2222 есть друзья 1111 (вы), 3333 (ваш другой друг) и 4444 (новый человек).

У человека 3333 есть друзья из 1111 (вас), 4444 (так же, как у человека 3333 - совпадения) и 5555.

Теперь, 2-я степень разделения (не то, что вы ищете) это то, что у человека 4444 есть друг 6666, 7777, 8888. Вам не безразличны эти другие (6666, 7777, 8888)

Вы ищете весь список друзей, которые не вы, и просто хотите увидеть Друзья 2222, 3333, 4444, 5555.

Я бы начал со списка только ваших друзей и использовал его в качестве основы для получения ИХ друзей. Самый внутренний запрос состоит в том, чтобы просто найти своих отличных друзей (без жесткого кодирования того, кем являются ВАШИ друзья). Тогда от того, что получить всех своих друзей. Если они окажутся похожими, «DISTINCT» отфильтрует это для вас. После того, как они выбраны, получите Союз ваших прямых друзей, чтобы представлять "AllFriends". Используя IF (), мы хотим, чтобы любой «другой» человек основывался на квалификации объединения. Если присоединяемый человек находится в положении 1, тогда мы хотим ДРУГОГО человека, и наоборот. После того, как у вас есть отдельный список друзей, ТОГДА присоедините его к таблице пользователей, чтобы получить их имя, фотографию и любую другую информацию профиля.

select
      AllFriends.FinalFriendID,
      CONCAT(U.User_FirstName, " ", U.User_LastName) AS User_FirstName, 
      U.User_ProfilePic 
   from
       ( select DISTINCT
               IF( F2.User_ID1 = YourDirectFriends.PrimaryFriend, F2.User_ID2, F2.User_ID1 ) 
                  as FinalFriendID
            from
               ( select DISTINCT 
                        IF( F.User_ID1 = YourID, F.User_ID2, F.User_ID1 ) as PrimaryFriendID
                   from
                      Friends F
                   where
                         F.user_ID1 = YourID
                      OR F.User_ID2 = YourID ) YourDirectFriends
                JOIN Friends F2
                   ON    YourDirectFriends.PrimaryFriendID = F2.User_ID1
                      OR YourDirectFriends.PrimaryFriendID = F2.User_ID2
         UNION 
         select DISTINCT
               IF( F.User_ID1 = YourID, F.User_ID2, F.User_ID1 ) as FinalFriendID
            from
               Friends F
            where
                  F.user_ID1 = YourID
               OR F.User_ID2 = YourID ) ) as AllFriends
        JOIN Users U
           on AllFriends.FinalFriendID = U.User_ID

О да, добавьте в свой квалификатор «Статус», где это применимо, и ваше предельное требование.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...