查询显示共同的朋友SQL Server

时间:2016-12-16 17:28:29

标签: sql-server

我正在asp.net c#中设计一个简单的社交网站,我需要一个帮助来查询查询中的共同朋友。

我的表是:

寄存器:

Register Table

朋友:

Friends Table

Friends表中, MyId FriendId 都是用户的ID。不同之处在于显示谁先与另一个人建立友谊。

我尝试写下面的一些查询,但它无法正常工作。

查询示例:

Select count(*) 
from Register R 
where RegisterId in (
                    (select F.FriendId as RegisterId 
                    from Friends f 
                    where f.MyId='" + Session["UserId"] + "' and f.Status=1) 
                    Union 
                    (select F.MyId as RegisterId 
                    from Friends f 
                    where f.FriendId='" + Session["UserId"] + "' and f.Status=1) 
                    union 
                    (select F.FriendId as RegisterId 
                    from Friends f 
                    where f.MyId='" + Session["UserId"] + "' and f.Status=1) 
                    union 
                    (select F.FriendId as RegisterId 
                    from Friends f 
                    where f.MyId='" + Session["UserId"] + "' and f.Status=1)
                    ) 
    and R.RegisterId !='" + Session["UserId"] + "' 
    and R.RegisterId !='" + Session["CurrentProfileId"] + "'

1 个答案:

答案 0 :(得分:2)

所以,共同的朋友,你的意思是,如果爱丽丝知道鲍勃,而爱丽丝知道查理,那么你想要识别鲍勃&查理是潜在的朋友,因为他们在爱丽丝中有共同的朋友?

在视觉上,我们可以将其表示为:

Bob <==> Alice 

其中&lt; ==&gt;表示Friends表中的条目。同样:

Charlie <==> Alice 

爱丽丝对两个友谊都很常见,所以:

Bob <==> Alice <==> Charlie 

使用上面的视觉表示,您会注意到有两个&lt; ==&gt;符号,表示正在运行的Friends表有2个实例(别名)。

我们称他们为FriendLeftFriendRight。在字段MyId上加入这两个表,但要确保左表和右表之间的FriendId不同。如下面的查询:

select 
    RegisterLeft.RegisterId as PotentialFriend1Id 
  , RegisterLeft.Name as PotentialFriend1Name 
  , RegisterRight.RegisterId as PotentialFriend2Id 
  , RegisterRight.Name as PotentialFriend2Name 
  , MutualFriend.Name as MutualFriendName 
from ##Register as RegisterLeft 
  inner join ##Friends as FriendLeft 
    on RegisterLeft.RegisterId = FriendLeft.FriendId 
  inner join ##Friends as FriendRight 
    on FriendLeft.MyId = FriendRight.MyId     -- join left and right instances of the friendship table 
  inner join ##Register as RegisterRight 
    on FriendRight.FriendId = RegisterRight.RegisterId 
  inner join ##Register as MutualFriend 
    on FriendLeft.MyId = MutualFriend.RegisterId 
where FriendLeft.FriendId != FriendRight.FriendId -- but eliminate rows that return the same person 

样本数据&amp;结果

对于给定的样本数据:

create table ##Register 
( 
    RegisterId int 
  , Name varchar(50) 
) 
go 

insert into ##Register values (1, 'Alice') 
insert into ##Register values (2, 'Bob') 
insert into ##Register values (3, 'Charlie') 
go 

create table ##Friends 
( 
    MyId int 
  , FriendId int 
) 
go 

insert into ##Friends values (1, 2) -- Alice   <==> Bob 
insert into ##Friends values (1, 3) -- Alice   <==> Charlie 
insert into ##Friends values (2, 1) -- Bob     <==> Alice 
insert into ##Friends values (3, 1) -- Charlie <==> Alice 
go 

我们得到的结果是:

PotentialFriend1Id PotentialFriend1Name  PotentialFriend2Id PotentialFriend2Name  MutualFriendName
------------------ --------------------- ------------------ --------------------- -----------------
3                  Charlie               2                  Bob                   Alice
2                  Bob                   3                  Charlie               Alice

以上正确地将Bob和Charlie视为爱丽丝作为共同朋友的潜在朋友。

交换友谊

请注意我在Friends表格中输入数据的方式 - 我已经反映了两种方式将友谊视为可交换的关系,因此一对(1, 2)是由等效的(2, 1)匹配。这使查询更简单。

这也意味着结果为你提供了两行潜在的友谊 - 正如我们上面所见,我们得到(Bob, Charlie)(Charlie, Bob)的结果,这基本上是潜在的友谊。鉴于您正在为Bob或Charlie运行此查询(以显示&#34;您可能知道的人的列表&#34;),无论如何您将在字段PotentialFriend1Id上进行过滤。如果您不是,那么您可以通过添加一个子句来确保PotentialFriend1Id小于(或大于)PotentialFriend2Id来消除这种倍增。这两种情况都会删除加倍的结果。

如果您不希望在Friends表格中存储两行,以表示单个友谊以表示每个方向的友谊,那么您的查询将依赖于添加朋友的顺序,以及识别共同的朋友变得更加困难。在这种情况下,创建反映关系的视图可能更容易:

CREATE VIEW FriendsReflected AS 
SELECT MyId, FriendId FROM Friends 
UNION 
SELECT FriendId, MyId FROM Friends 

这样,如果Friends仅包含{ (1, 2) ; (1, 3) },则视图将返回{ (1, 2) ; (1, 3) ; (2, 1) ; (3, 1) }

朋友之友

您已经在下面澄清过,如果Bob与Alice和Charlie都是朋友,如果David是Bob的朋友,那么David应该看到Alice和Charlie,他们是David的朋友Bob的朋友。

给出以下样本数据:

insert into ##Register values (1, 'Alice') 
insert into ##Register values (2, 'Bob') 
insert into ##Register values (3, 'Charlie') 
insert into ##Register values (4, 'David') 
go 

insert into ##Friends values (1, 2) -- Alice   <==> Bob 
insert into ##Friends values (3, 2) -- Charlie <==> Bob 
insert into ##Friends values (2, 1) -- Bob     <==> Alice 
insert into ##Friends values (2, 3) -- Bob     <==> Charlie 
insert into ##Friends values (2, 4) -- Bob     <==> David 
insert into ##Friends values (4, 2) -- David   <==> Bob 
go 

这模仿以下关系,大卫是鲍勃的朋友,鲍勃是爱丽丝和查理的朋友:

David <==> Bob <==> Alice 
David <==> Bob <==> Charlie 

然后,以下查询是您需要的:

select 
    Myself.Name as Myself 
  , MyFriendsDetails.Name as MyFriend 
  , TheirFriendDetails.Name as MyFriendsFriends 
from ##Register as Myself 
  inner join ##Friends as MyFriends 
    on Myself.RegisterId = MyFriends.MyId 
  inner join ##Register as MyFriendsDetails 
    on MyFriends.FriendId = MyFriendsDetails.RegisterId 
  inner join ##Friends as TheirFriends 
    on MyFriends.FriendId = TheirFriends.MyId 
  inner join ##Register as TheirFriendDetails 
    on TheirFriends.FriendId = TheirFriendDetails.RegisterId 
where Myself. RegisterId != TheirFriendDetails.RegisterId -- Remove cases where the friend's friend is myself 

因为它返回以下结果,其中第一列显示为个人,第二列显示为他/她的朋友,第三列显示为朋友的朋友。最后两行专门展示了David的情况。然而,总的来说,鲍勃是一个明星的中心,有三个链接到他的3个朋友,爱丽丝,查理和大卫。通过Bob,他们每个都暴露给另外2个,这就是为什么结果总共有6行。

Myself        MyFriend      MyFriendsFriends
------------- ------------- --------------------
Alice         Bob           Charlie
Alice         Bob           David
Charlie       Bob           Alice
Charlie       Bob           David
David         Bob           Alice
David         Bob           Charlie