第一件事是你不应该执行我的第一个答案。相反,您应该更改或限制您的架构。我的建议见下文。
据我了解您的架构,它是:
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. */
这更容易处理。您也可以将其重写为一系列连接,但我怀疑作为第一步,使用in
和not in
这样的子句更易于阅读。
修改后的 sqlfiddle: http ://sqlfiddle.com/#!2/92ff2/1
(我必须使用大型数据集对其进行测试,但我的直觉认为连接会更快——但对于这样的代码,我怀疑学习/获得正确答案比最初优化速度更重要。)