分离度查询

时间:2012-02-27 16:20:06

标签: mysql sql

我有一个成员到成员的连接表。架构是member_id,friend_id,is_active。我想建立一个朋友朋友的成员联系列表。我不太确定如何处理查询,更不用说以半优化的方式了。

上表的工作方式是member_id和friend_id在另一个表上基本相同。在我的系统中,除了这一个表之外,这些id通常被称为member_id。例如,假设我的member_id是21.我的号码可以在无限量的其他行上作为member_id或friend_id,它或者基于谁最初发起了实际的友谊请求,那我并不想要冗余数据我有欺骗行基本上做同样的事情。

我希望有一个查询,我不仅可以建立一个学位水平(想想LinkedIn),但我也可以确定一个人可能有多少共同的朋友正在展示(想想Facebook)。这里的x因子是我之前提到的is_active列。此列可以是0或1.这是一个简单的tinyint列,可用作开/关开关。任何与1的朋友联系都是积极的友谊,而0正在等待。我需要将此查询基于我的活跃朋友和他们活跃的朋友等等。我的朋友们中没有一个活跃的朋友都是我的活跃朋友。

如何构建这样的查询(即使我无法显示分离级别,只能获得相互计数)?现在,我可以想到一些东西,但它涉及查询后查询一些嵌套在循环中,是的,我只是无法想象对我的服务器的整体性能或健康随着时间的推移有任何好处。

3 个答案:

答案 0 :(得分:7)

以下是使用JOIN使用广度优先,最短路径搜索执行搜索的方法。这个算法没有神奇之处,因为我们使用MySQL来找到我们的答案,而且我们没有使用任何使用任何启发式或优化的花式搜索算法。

我的'朋友'表具有单向关系,因此我们确实存在重复,即存储“1到2”和“2到1”。我也排除了is_active,因为实现很明显:

以下是数据:

member_id   friend_id
1           2
1           3
1           4
2           1
2           3
2           5
2           6
3           2
3           1
4           1
5           2
6           2
6           7
7           6
7           8
8           7

我们选择了会员1,我们要求的是7位朋友,7位朋友,朋友等等?计数为0表示否,计数为1表示是。

SELECT COUNT(*)
FROM friends f1
WHERE f1.member_id = 1
  AND f1.friend_id = 7

如果不是,那么他们是朋友的朋友吗?

SELECT COUNT(*)
FROM friends f1
JOIN friends f2
  ON f2.member_id = f1.friend_id
WHERE f1.member_id = 1
  AND f2.friend_id = 7

如果不是,那么朋友的朋友呢?

SELECT COUNT(*)
FROM friends f1
JOIN friends f2
  ON f2.member_id = f1.friend_id
JOIN friends f3
  ON f3.member_id = f2.friend_id
WHERE f1.member_id = 1
  AND f3.friend_id = 7

等等......

第三个查询将找到路径“1到2”,“2到6”和“6到7”,返回计数为1。

每个查询都变得更加昂贵(由于连接数量较多),因此您可能希望在某个时刻限制搜索。一个很酷的事情是这个搜索从两端向中间工作,这是一个简单的优化建议用于最短路径搜索。

以下是如何找到成员1的共同朋友推荐:

SELECT f2.friend_id
FROM friends f1
JOIN friends f2
  ON f2.member_id = f1.friend_id
LEFT JOIN friends f3
  ON f3.member_id = f1.member_id
  AND f3.friend_id = f2.friend_id
WHERE f1.member_id = 1
  AND f2.friend_id <> f1.member_id // Not ourself
  AND f3.friend_id IS NULL // Not already a friend

答案 1 :(得分:2)

如果没有表格的具体内容,我可以提供以下指导...如果你运行你的查询总是把LOWER ID放在第一个位置,并做不同的(甚至计算看看普通人有多频繁/可能是对于其他方),你会消除臃肿。

前:

select
      case when table.MemberID < table.FriendID
         then table.MemberID else table.FriendID end as FirstPerson,
      case when table.MemberID < table.FriendID
         then table.FriendID else table.MemberID end as SecondPerson
   from
     ...
   where...

所以,如果你的数据有

member ID   Friend ID
1           2
1           3
1           4
2           1
2           3
2           5
3           2
5           2

and you queried for friends / associations with member ID 1 you would start with
1  2
1  3
1  4

but then friendships from ID #2 would return
1  2  (reversal of 2 / 1 entry) would be duplicate
2  3
2  5

then from friendship 3
2  3  (reversal of 3 / 2 entry) would be duplicate

then from friendship 5 from member 2
2  5  (reversal of 5 / 2 entry) would be dupliate

不确定这正是您所寻找的,但听起来类似于其他“社交网络”寻找朋友/协会。至于一个人的关联/友谊中有多少“度”,你可能需要嵌套你的查询,或至少在一些循环结构中继续查询。

答案 2 :(得分:1)

为了进一步改进已接受的答案,您可以利用合并来检查每个分离程度,直到找到它为止。 e.g:

SELECT COALESCE( (SELECT 1 FROM friends f1 WHERE f1.member_id = 1 AND f1.friend_id = 7 LIMIT 1), (SELECT 2 FROM friends f1 JOIN friends f2 ON f2.member_id = f1.friend_id WHERE f1.member_id = 1 AND f2.friend_id = 7 LIMIT 1) /*, ..ETC* ) as degrees_away