MySQL Query查找朋友和共同朋友的数量

时间:2012-10-26 15:59:37

标签: mysql

我已经查看过这些问题,但是我找不到任何能够完全符合我需要的东西,而且我无法弄清楚如何自己做。

我有2个表,一个用户表和一个朋友链接表。用户表是我所有用户的表格:

    +---------+------------+---------+---------------+
    | user_id | first_name | surname |     email     |
    +---------+------------+---------+---------------+
          1         joe       bloggs    joe@test.com
          2         bill      bloggs    bill@test.com
          3         john      bloggs    john@test.com
          4         karl      bloggs    karl@test.com

我的朋友链接表然后显示用户之间的所有关系,例如:

    +--------=+---------+-----------+--------+
    | link_id | user_id | friend_id | status |
    +---------+---------+-----------+--------+
       1         1          3           a
       2         3          1           a
       3         4          3           a
       4         3          4           a
       5         2          3           a
       6         3          2           a

作为注释,状态栏中的a表示已批准,也可能有r(请求)和d(拒绝)。

我想要做的是查询,如果用户进行搜索,它将返回他们当前不是朋友的用户列表以及每个用户与他们共享的共享好友数量。

我设法为所有当前不与他们成为朋友的用户进行查询。因此,如果进行搜索的用户的用户ID为1:

SELECT u.user_id,u.first_name,u.surname
FROM users u
    LEFT JOIN friend_links fl
        ON u.user_id = fl.user_id AND 1 IN (fl.friend_id)
WHERE fl.friend_id IS NULL
AND u.user_id != 1
AND surname LIKE 'bloggs'

我如何计算每个返回用户的共同朋友数量?

修改

就像编辑一样,我不认为我对我的问题特别清楚。

我目前拥有的查询将产生以下结果集:

    +---------+------------+---------+
    | user_id | first_name | surname |
    +---------+------------+---------+
          2         bill      bloggs
          4         karl      bloggs

这些是与姓氏博客匹配的用户,这些博客目前不是joe bloggs(用户ID 1)的朋友。

然后我希望这些用户中有多少共同朋友与用户进行搜索,以便返回的结果如下所示:

    +---------+------------+---------+--------+
    | user_id | first_name | surname | mutual |
    +---------+------------+---------+--------+
          2         bill      bloggs     1
          4         karl      bloggs     1

每个返回的用户都有1个共同的朋友,因为joe bloggs(用户ID 1)是john bloggs的朋友,john bloggs是两个返回用户的朋友。

我希望这更清楚一点。

感谢。

3 个答案:

答案 0 :(得分:5)

可以通过在friend_id字段上将friend_links表连接到自己来找到相互朋友,如下所示:

SELECT *
FROM friend_links f1 INNER JOIN friend_links f2
  ON f1.friend_id = f2.friend_id
WHERE f1.user_id = $person1
  AND f2.user_id = $person2

但请记住,在最糟糕的情况下,这基本上是平方在friend_links表中的行数,并且一旦你有一个非平凡的数量,它可以很容易地提升你的服务器行。一个更好的选择是为每个用户使用2个子查询,然后加入那些结果。

SELECT *
FROM (
  SELECT *
  FROM friend_links
  WHERE user_id = $person1
) p1 INNER JOIN (
  SELECT *
  FROM friend_links
  WHERE user_id = $person1
) p2
  ON p1.friend_id = p2.friend_id

此外,您可以通过删除代理键link_id并仅使(user_id,friend_id)成为主键来简化您的friend_links表,因为它们必须是唯一的。


编辑:

  

如何将此应用于搜索不是朋友的用户的原始查询,如果可能,我想在单个查询中同时执行这两项操作?

SELECT f2.user_id, COUNT(*) 'friends_in_common'
FROM friend_links f1 LEFT JOIN friend_links f2
  ON f1.friend_id = f2.friend_id
WHERE f1.user_id = $person
GROUP BY f2.user_id
ORDER BY friends_in_common DESC
LIMIT $number

我也在考虑user_id约束可以从WHERE子句转移到JOIN条件,以减少自连接创建的数据集的大小并排除在我的第二个例子中使用子查询。

答案 1 :(得分:2)

此查询列出了与用户1不是朋友且姓氏与'%bloggs%'匹配的任何人:

SELECT
  users.user_id,
  users.first_name,
  users.surname,
  Sum(IF(users.user_id = friend_links_1.friend_id, 1, 0)) As mutual
FROM
  users inner join
    (friend_links INNER JOIN friend_links friend_links_1
     ON friend_links.friend_id = friend_links_1.user_id)
  ON friend_links.user_id=1 AND users.user_id<>1
WHERE
  users.surname LIKE '%bloggs%'
GROUP BY
  users.user_id, users.first_name, users.surname
HAVING
  Sum(IF(users.user_id = friend_links.friend_id, 1, 0))=0

只需更改ON子句上的用户ID,以及WHERE子句上的姓氏。我认为它现在应该正常工作!

答案 2 :(得分:0)

如果A是B的朋友,那么B也是A的朋友?使用一个链接而不是两个链接(而不是在friends_links中的两行)不是更好吗?然后你必须使用两个状态列,status1和status2,只有当status1 = status2 =“a”时,A才是B的朋友。

有许多方式可以表达共同的朋友,例如:

SELECT friend_id
FROM friend_links
WHERE friend_links.user_id = $user1 or friend_links.user_id = $user2
  AND NOT (friend_links.friend_id = $user1 or friend_links.friend_id = $user2)
GROUP BY friend_id
HAVING Count(*)>1

此查询显示每个用户以及不是他/她的朋友的任何人:

SELECT
  users.user_id,
  users.first_name,
  users_1.user_id,
  users_1.first_name
FROM
  users INNER JOIN users users_1 ON users.user_id <> users_1.user_id
WHERE
  NOT EXISTS (SELECT *
              FROM friend_links
              WHERE
                friend_links.user_id = users.user_id
                AND friend_links.friend_id = users_1.user_id)

(唯一认为我没有检查的是友谊状态,但添加该检查很容易)。

我还在努力,但要将这两个问题很好地结合起来并不容易。所以这不是一个答案,我只是展示了一些我尝试过的想法。

但你到底需要什么?一个查询,返回每个用户,不是他/她的朋友和共同的朋友数,或者已经给出了user_id?

使用一些代码来回答你的问题并不是一个问题...但只需使用SQL就可以有一个很好的方法! :)

修改

我仍然想知道是否有更好的解决方案,特别是下一个查询可能会非常慢,但看起来这可能有效:

SELECT
  users_1.user_id,
  users_2.user_id,
  Sum(IF(users_1.user_id = friend_links.user_id AND users_2.user_id = friend_links_1.friend_id, 1, 0)) As CommonFriend
FROM
  users users_1 INNER JOIN users users_2
    ON users_1.user_id <> users_2.user_id,
  (friend_links INNER JOIN friend_links friend_links_1
    ON friend_links.friend_id = friend_links_1.user_id)
GROUP BY
  users_1.user_id,
  users_2.user_id
HAVING
  Sum(IF(users_1.user_id = friend_links.user_id AND users_2.user_id = friend_links.friend_id, 1, 0))=0

(和以前一样,我没有检查友谊状态)

如果给出了用户,你可以放WHERE users_1.user_id=$user1,但最好留下一个用户表,并过滤掉该用户的下一个INNER JOIN。