0

我必须从 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. 我已经阻止的用户,即 MY_ID 和 friends_friend_id = 3 的 Friends.Status,我也有一个,用户 id 3333 是 2222 的朋友,我已经阻止了他,但他在列表中。

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

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

4

2 回答 2

3

第一件事是你不应该执行我的第一个答案。相反,您应该更改或限制您的架构。我的建议见下文。

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

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;

我的好友好友ID列表如下:

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 Fiddle 供参考并显示结果: http ://sqlfiddle.com/#!2/e6376/13


编辑:只是补充一下我将如何更改此架构。

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

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

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

在谷歌的情况下,你已经完成了。在 Facebook 的案例中,我会使用这种结构,但要求对于每个关系,表中有两行。因此,如果“1212”是带有“0415”的 Facebook 朋友,则有 ('1212', '0415') 和 ('0415','1212') 的 (individual_userid,friend_userId) 行。确保其工作和维护将需要用于插入/删除的存储过程,以确保添加和删除两行。(没有更新——这些是唯一的 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

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

于 2012-03-05T17:08:53.607 回答
1

正如迈克的回答所解释的那样,我也是如此,您的表结构类似于

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 的朋友。

现在,第二级分离(不是你要找的)是那个人 4444 有 6666、7777、8888 的朋友。你不关心其他人(6666、7777、8888)

您正在查找不是您的朋友的完整列表,并且只想查看 Friends 2222、3333、4444、5555。

我会从你的朋友列表开始,并以此为基础来获得他们的朋友。最内在的查询是获取您不同的朋友(无需硬编码您的朋友是谁)。然后从那里,得到他们所有的朋友。如果它们碰巧有相似之处,“DISTINCT”将为您过滤掉。选择这些后,让您的直接朋友的 UNION 代表“AllFriends”。通过使用 IF(),我们希望“其他”人基于加入资格。如果加入的人在位置 1,那么我们需要 OTHER 人,反之亦然。一旦你有了不同的朋友列表,然后将其加入用户表以获取他们的姓名、照片和任何其他个人资料信息。

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

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

于 2012-03-05T17:46:17.713 回答