【问题标题】:Degrees of Separation Query分离度查询
【发布时间】:2012-03-17 02:57:39
【问题描述】:

我有一个成员到成员连接表。架构是 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 是待处理的。我需要根据我的活跃朋友和他们的活跃朋友等来进行此查询。我朋友的活跃朋友都不是我的活跃朋友。

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

【问题讨论】:

  • 似乎对于大多数“最短路径”算法来说,单向路径使事情变得简单得多,所以不要太担心重复。

标签: mysql sql


【解决方案1】:

以下是使用 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,我们问的是 1 和 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

【讨论】:

  • 与 COALESCE 结合使用时会派上用场
【解决方案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

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

【讨论】:

  • 这很有帮助,您还需要了解哪些更多类型的东西才能帮助我想出一个独特的解决方案?至于提到“社交网络”是的,从概念上讲,其中一些更像是一种严格意义上的东西,因为我也只是为了学习而努力学习。
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-11
    • 2012-01-12
    • 2011-10-12
    • 1970-01-01
    • 1970-01-01
    • 2014-11-10
    • 1970-01-01
    • 2014-01-01
    相关资源
    最近更新 更多