简体   繁体   English

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

[英]MySQL Query to find friends and number of mutual friends

I have looked through the questions but I cant find anything that does exactly what I need and I can't figure out how to do it myself. 我已经查看了问题,但我找不到任何完全符合我需要的东西,我无法弄清楚如何自己做。

I have 2 tables, a user table and a friend link table. 我有2个表,一个用户表和一个朋友链接表。 The user table is a table of all my users: 用户表是我所有用户的表:

    +---------+------------+---------+---------------+
    | 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

My friend links table then shows all relationships between the users, for example: 我的朋友链接表然后显示用户之间的所有关系,例如:

    +--------=+---------+-----------+--------+
    | 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

As a note the a in the status column means approved, there could also be r(request) and d(declined). 作为注释,状态栏中的a表示已批准,也可能有r(请求)和d(拒绝)。

What I want to do is have a query where if a user does a search it will bring back a list of users that they are currently not already friends with and how many mutual friends each user has with them. 我想要做的是查询如果用户进行搜索,它将返回他们当前不是朋友的用户列表以及每个用户与他们有多少共同朋友。

I have managed to get a query for all users that are currently not friends with them. 我已设法获取当前不与他们成为朋友的所有用户的查询。 So if the user doing the search had the user id of 1: 因此,如果执行搜索的用户的用户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'

How then do I have a count of the number of mutual friends for each returned user? 那么我如何计算每个返回用户的共同朋友数?

EDIT: 编辑:

Just as an edit as I don't think I am being particularly clear with my question. 就像编辑一样,我不认为我对我的问题特别清楚。

The query that I currently have above will produce the following set of results: 我上面的查询将产生以下结果集:

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

Those are the users matching the surname bloggs that are not currently friends with joe bloggs (user id 1). 这些是与姓氏博客匹配的用户,这些博客目前不是joe bloggs(用户ID 1)的朋友。

Then I want to have how many mutual friends each of these users has with the user doing the search so the returned results would look like: 然后我希望这些用户中有多少共同的朋友与进行搜索的用户有关,因此返回的结果如下所示:

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

Each of these returned users has 1 mutual friend as joe bloggs (user id 1) is friends with john bloggs and john bloggs is friends with both returned users. 每个返回的用户都有1个共同的朋友,因为joe bloggs(用户ID 1)是john bloggs的朋友,john bloggs是两个返回用户的朋友。

I hope this is a bit more clear. 我希望这更清楚一点。

Thanks. 谢谢。

Mutual friends can be found by joining the friend_links table to itself on the friend_id field like so: 可以通过在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

But bear in mind that this, in its worst case, is essentially squaring the number of rows in the friend_links table and can pretty easily jack up your server once you have a non-trivial number of rows. 但请记住,在最糟糕的情况下,这基本上是对friend_links表中的行数进行平方 ,并且一旦您拥有非平凡的行数,就可以非常轻松地将服务器填满。 A better option would be to use 2 sub-queries for each user and then join the results of those. 更好的选择是为每个用户使用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

Also, you can simplify your friend_links table by removing the surrogate key link_id and just making (user_id,friend_id) the primary key since they must be unique anyway. 此外,您可以通过删除代理键link_id并仅将(user_id,friend_id)作为主键来简化您的friend_links表,因为它们必须是唯一的。


Edit: 编辑:

How would this be applied to the original query of searching for users that aren't already friends, I would like to do both in a single query if possible? 如何将此应用于搜索不是朋友的用户的原始查询,如果可能,我想在单个查询中同时执行这两项操作?

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

I am also thinking that the user_id constraints can be moved from the WHERE clause into the JOIN conditions to reduce the size of the data set created by the self-join and preclude the use of subqueries like in my second example. 我也在考虑user_id约束可以从WHERE子句移动到JOIN条件,以减少自连接创建的数据集的大小,并排除使用子查询,如我的第二个示例。

This query lists anyone who's not friend with user 1 and whose surname matches '%bloggs%' : 此查询列出了与用户1不是朋友且姓氏与'%bloggs%'匹配'%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

just change the user id on the ON clause, and the surname on the WHERE clause. 只需更改ON子句上的用户ID,以及WHERE子句上的姓氏。 I think it should work correctly now! 我认为它现在应该正常工作!

If A is friend of B, then B is also a friend of A? 如果A是B的朋友,那么B也是A的朋友? Wouldn't it be better to use just a link instead of two links (and instead of two rows in friends_links)? 使用一个链接而不是两个链接(而不是在friends_links中的两行)不是更好吗? Then you have to use two status columns, status1 and status2, and A is friend of B only if status1 = status2 = "a". 然后你必须使用两个状态列,status1和status2,只有当status1 = status2 =“a”时,A才是B的朋友。

There are many ways to show mutual friends, eg: 有很多方式可以展示共同的朋友,例如:

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

And this query shows for each user and anyone who's not his/her friend: 此查询显示每个用户和任何不是他/她的朋友的人:

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)

(The only think I didn't check is the friendship status, but it's easy to add that check). (唯一认为我没有检查的是友谊状态,但很容易添加该检查)。

I'm still working on it, but it's not easy to combine nicely these two queries togheter. 我还在努力,但要将这两个查询很好地结合起来并不容易。 So this isn't exactly an answer, I'm just showing some ideas that i've tried. 所以这不是一个答案,我只是展示了我尝试过的一些想法。

But what do you need exactly? 但你到底需要什么? A query that returns every user with anyone who's not his/her friend and the number of friends in common, or is the user_id already given? 一个查询,返回每个用户,不是他/她的朋友和共同的朋友数,或者已经给出了user_id?

With some code it's not a problem to answer your question... but there has to be a nice way just by using SQL! 使用一些代码来回答你的问题并不是一个问题...但只需使用SQL就可以有一个很好的方法! :) :)

EDIT: 编辑:

I'm still wondering if there's a better solution to this, in particular the next query could be extremely slow, but it looks like this might work: 我仍然想知道是否有更好的解决方案,特别是下一个查询可能会非常慢,但看起来这可能会起作用:

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

(as before, i didn't check friendship status) (和以前一样,我没有检查友谊状态)

If user is given, you could put WHERE users_1.user_id=$user1 but it's better to just leave one user table, and filter the next INNER JOIN whith that user. 如果给出了用户,你可以把WHERE users_1.user_id=$user1放在最好只留下一个用户表,并过滤掉那个用户的下一个INNER JOIN。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM