9

我已经浏览了这些问题,但我找不到任何能完全满足我需要的东西,而且我自己也不知道该怎么做。

我有 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

这些是与姓氏 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 是两个返回用户的朋友。

我希望这更清楚一点。

谢谢。

4

3 回答 3

6

可以通过在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条件中,以减少自连接创建的数据集的大小,并防止像在我的第二个示例中那样使用子查询。

于 2012-10-26T17:40:58.670 回答
3

此查询列出了不是用户 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 子句上的姓氏。我认为它现在应该可以正常工作了!

于 2012-10-29T10:51:06.160 回答
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。

于 2012-10-27T14:26:11.623 回答