推荐朋友的Mysql查询帮助

时间:2012-03-05 15:48:11

标签: mysql join sql sql-subselect

我必须从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 for MY_ID和friends_friend_id = 3,我也有一个,用户ID 3333是2222的朋友,我已经阻止了他,但他已经出现在列表中。< / LI>

    如果通过IN(1111,2222)进行搜索将来会导致某些问题,请指导我,因为朋友数肯定会增加。在上述查询之前,我使用group_concat以逗号分隔的朋友列表。所有这些都在存储过程中。

    我希望我能清楚地解释这个问题。

2 个答案:

答案 0 :(得分:2)

首先,你不应该实现我的第一个答案。相反,您应该更改或约束您的架构。请参阅下面的我的建议。

据我了解您的架构,它是:

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

如果有朋友关系,其中一个id位于第1位,1位于第2位。

现在,假设我的id为1212,我朋友的id列表是:

 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创建in-clause。尽管这里的篇幅很长,但速度更快。

您可以询问各个部分的作用。但同样,我的建议是不要这样做。这就是为什么良好的桌面设计使事情变得容易的原因。

SQL小提琴,供参考和显示结果:http://sqlfiddle.com/#!2/e6376/13


编辑:只是添加有关如何更改此架构的信息。

目前还不清楚在您的应用中,朋友之间的关系是Google(允许使用非对称关系)还是Facebook关系(仅允许对称关系)。

在这两种情况下,我都会将架构更改为:

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

在谷歌案例中,你已经完成了。在Facebook的情况下,我会使用这种结构,但要求对于每个关系,两行都会进入表格。因此,如果'1212'是Facebook好友w /'0415',则有(individual_userid,friend_userId)行('1212','0415')&amp; ( '0415', '1212')。确保其工作和维护将需要插入/删除的存储过程以确保添加和删除这两行。 (没有更新 - 这些是唯一的ID。)

如果我们确定维护这些关系并且启动关系的朋友总是出现在individual_userId中,那么,我的最终查询将变为:

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. */

这更容易处理。您也可以将其重写为一系列连接,但我怀疑,作为第一步,使用innot in这样的子句更容易阅读。

修改了sqlfiddle:http://sqlfiddle.com/#!2/92ff2/1

(我必须使用大型数据集对其进行测试,但我的直觉说联接会更快 - 但对于这样的代码,我怀疑学习/获得正确的答案比最初优化更重要速度。)

答案 1 :(得分:1)

正如Mike的回答所解释的那样,我的表结构就像

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

你想要的是一个DISTINCT朋友列表,他们是你的朋友或与你的朋友直接相关的朋友(即:与你分开1度)。所以,让我们继续这个场景。

您是第1111号人,并有朋友2222和3333.

人2222有1111(你),3333(你的另一个朋友)和4444(新人)的朋友。

人3333有1111(你)的朋友,4444(与3333的朋友 - 巧合相同)和5555。

现在,分离(不是你正在寻找)的第二度是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

哦,是的,在适用的情况下添加“状态”限定符,以及您的限制要求。