【问题标题】:MySQL Query to find friends and number of mutual friendsMySQL 查询以查找朋友和共同朋友的数量
【发布时间】:2012-10-26 15:59:37
【问题描述】:

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

我有 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(request) 和 d(declined)。

我想要做的是有一个查询,如果用户进行搜索,它将返回一个他们目前还不是朋友的用户列表以及每个用户与他们有多少共同朋友。

我设法查询了所有当前不是他们朋友的用户。因此,如果进行搜索的用户的用户 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 是两个返回用户的朋友。

我希望这更清楚一点。

谢谢。

【问题讨论】:

    标签: mysql


    【解决方案1】:

    可以通过在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) 设为主键来简化您的朋友链接表,因为它们无论如何都必须是唯一的。


    编辑:

    这将如何应用于搜索尚未成为朋友的用户的原始查询,如果可能的话,我想在一个查询中进行这两项操作?

    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 条件中,以减少自连接创建的数据集的大小并排除使用子查询,如我的第二个例子。

    【讨论】:

    • 这将如何应用于搜索尚未成为朋友的用户的原始查询,如果可能的话,我想在一个查询中进行这两项操作?我还想要一些共同的朋友而不是返回的名称,例如查询将返回用户的姓名和该人拥有的共同朋友的数量。还要感谢有关简化friend_links 表的提示,我会将其放入。
    • 经过一些测试,这似乎工作得很好,非常感谢我的朋友!
    • 当我将它应用到我的实际数据中时,我发现只有当他们有共同的朋友时才会带回人们,如果他们没有共同的朋友,那么那个人不在结果集中这不是我想要的。
    • INNER JOIN 更改为LEFT JOIN
    • 在您回复的最后一个查询中没有内部联接。我想我可能对你的意思感到困惑?
    【解决方案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 子句中的姓氏。我认为它现在应该可以正常工作了!

    【讨论】:

    • 这似乎工作得很好,非常感谢你的帮助,你是个天才。
    • @fthiella 如何过滤已经是我朋友的朋友?
    【解决方案3】:

    如果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。

    【讨论】:

    • 我最初确实只有朋友 A 和朋友 B 的单一记录,但实际上有很多查询,拥有 2 条记录更容易和更快。例如,仅查询朋友的速度要快得多,只需查询单个列,而不必查询 2。我理想的查询是有一个搜索查询,返回当前用户还不是朋友的所有用户以及每个返回用户的一些共同朋友。
    • 你试过我编辑的答案了吗?如果这是您需要的,可以稍微优化一下!
    • 我试了一下,它确实有效,但它并没有完全返回我想要的。抱歉不清楚,很难解释。请看我的编辑,希望这会澄清一些事情。感谢您的宝贵时间。
    • 我认为现在很清楚了...这简化了一些事情...我会发布一个新答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-15
    • 1970-01-01
    相关资源
    最近更新 更多