我必须从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是我的朋友,我想让所有朋友都很好,但我想要的是:
如果通过IN(1111,2222)
进行搜索将来会导致某些问题,请指导我,因为朋友数肯定会增加。在上述查询之前,我使用group_concat
以逗号分隔的朋友列表。所有这些都在存储过程中。
我希望我能清楚地解释这个问题。
答案 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')& ( '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. */
这更容易处理。您也可以将其重写为一系列连接,但我怀疑,作为第一步,使用in
和not 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
哦,是的,在适用的情况下添加“状态”限定符,以及您的限制要求。