SQL选择您可能认识的人

时间:2012-06-11 12:27:20

标签: mysql sql

The question you're asking appears subjective and is likely to be closed.

当我在标题字段填写时看到上面的可怕警告时,我并不感到惊讶。

我几乎阅读了所有讨论friends of friendsmutual friends的帖子,但我不确定我找到了正确的解决方案。

对不起,我不擅长英语,也不擅长SQL。

如何在不擅长这两种语言的同时找到正确的答案?

我决定要问。我不会让自己失望down-vote或任何duplication warning

正如我想要的答案,我会尽可能真诚地写下来,因为任何进一步的类似问题都可以得到帮助。

我有朋友关系表。

FRIEND (TABLE)
-----------------------------------
PLAYER_ID(PK,FK)   FRIEND_ID(PK,FK)
-----------------------------------
1                  2                 // 1 knows 2
2                  1                 // 2 knows 1
1                  3                 // 1 knows 3
2                  3                 // 2 knows 3
2                  4                 // 2 knows 4
2                  5                 // 2 knows 5 // updated
3                  5                 // 3 knows 5 // updated
1                  100
1                  200
1                  300
100                400
200                400
300                400

两个composite primary keys也是来自PLAYER表的外键。

我问过并从这些善良的人那里得到了答案,因为“人们互相认识”。

SQL view for acquaintance from table

我有这样的观点。

ACQUAINTANCE (VIEW)
-----------------------------------
PLAYER_ID(PK,FK)   FRIEND_ID(PK,FK)
-----------------------------------
1                  2                 // 1 knows 2
2                  1                 // 2 knows 1

您可能会注意到,这种关系的业务逻辑有两个目的。

  1. 一个玩家可以说他或她认识别人。
  2. 当两个人都说他们彼此认识时,他们可以说是熟人。
  3. 而且,现在,我想知道

    有什么好方法
    1. 选择其他PLAYER_ID
    2. 给定的PLAYER(PLAYER_ID)(比如1)
    3. 每个人都是“给予PLAYER直接朋友”的朋友之一
    4. 每个人不是玩家本人(不包括1 - > 2 - > 1)
    5. 哪一个不是PLAYER的直接朋友(不包括3从1 - > 2 - > 3 by 1 - > 3)
    6. 如果可能,按共同朋友的数量排序。
    7. 我认为Justin Niessner在"people you may know" sql query中的答案是我必须遵循的最接近的路径。

      提前致谢。

      如果这个主题真的重复而且没有必要,我将关闭该主题。

      更新---------------------------------------------- ----------------

      对于RaphaëlAlthaus的评论whose name is same with my future daughter(这是男孩的名字吗?),

      3是friends of friends of 1的候选人,因为

      1 knows 2
      2 knows 3
      

      但排除因为

      1 already knows 3
      

      基本上我想为given player

      服务
      people he or she may know
      which is not himself or herself // this is nothing but obvious
      which each is not already known to himself
      

      上表

      by 1 -> 2 -> 4 and 1 -> 3 -> 5
      
      4 and 5 can be suggested for 1 as 'people you may know'
      
      order by number of mutual friends will be perfect
      but I don't think I can understand even if someone show me how. sorry.
      

      谢谢。

      更新---------------------------------------------- -----------------------

      我想我必须从我所学到的FROM HERE WITH VARIOUS PEOPLE中逐步尝试,即使这不是正确的答案。 如果我做错了,请告诉我。

      首先,让我自己加入FRIEND表。

      SELECT *
      FROM FRIEND F1 INNER JOIN FRIEND F2 ON F1.FRIEND_ID = F2.PLAYER_ID
      

      打印

      +-----------+-----------+-----------+-----------+
      | PLAYER_ID | FRIEND_ID | PLAYER_ID | FRIEND_ID |
      +-----------+-----------+-----------+-----------+
      |         1 |         2 |         2 |         1 |
      |         1 |         2 |         2 |         3 |
      |         1 |         2 |         2 |         4 |
      |         1 |         2 |         2 |         5 |
      |         1 |         3 |         3 |         5 |
      |         2 |         1 |         1 |         2 |
      |         2 |         1 |         1 |         3 |
      |         2 |         3 |         3 |         5 |
      +-----------+-----------+-----------+-----------+
      

      仅限F2.FRIEND_ID

      SELECT F2.FRIEND_ID
      FROM FRIEND F1 INNER JOIN FRIEND F2 ON F1.FRIEND_ID = F2.PLAYER_ID
      

      打印

      +-----------+
      | FRIEND_ID |
      +-----------+
      |         1 |
      |         3 |
      |         4 |
      |         5 |
      |         5 |
      |         2 |
      |         3 |
      |         5 |
      +-----------+
      

      仅限1个

      SELECT F2.FRIEND_ID
      FROM FRIEND F1 INNER JOIN FRIEND F2 ON F1.FRIEND_ID = F2.PLAYER_ID
      WHERE F1.PLAYER_ID = 1;
      

      打印

      +-----------+
      | FRIEND_ID |
      +-----------+
      |         1 |
      |         3 |
      |         4 |
      |         5 |
      |         5 |
      +-----------+
      

      不是1

      SELECT F2.FRIEND_ID
      FROM FRIEND F1 INNER JOIN FRIEND F2 ON F1.FRIEND_ID = F2.PLAYER_ID
      WHERE F1.PLAYER_ID = 1 
      AND F2.FRIEND_ID != 1;
      

      打印

      +-----------+
      | FRIEND_ID |
      +-----------+
      |         3 |
      |         4 |
      |         5 |
      |         5 |
      +-----------+
      

      不是1的直接知识

      SELECT F2.FRIEND_ID
      FROM FRIEND F1 INNER JOIN FRIEND F2 ON F1.FRIEND_ID = F2.PLAYER_ID
      WHERE F1.PLAYER_ID = 1
      AND F2.FRIEND_ID != 1
      AND F2.FRIEND_ID NOT IN (SELECT FRIEND_ID FROM FRIEND WHERE PLAYER_ID = 1);
      

      打印

      +-----------+
      | FRIEND_ID |
      +-----------+
      |         4 |
      |         5 |
      |         5 |
      +-----------+
      

      我想我到了那儿。

      更新---------------------------------------------- -------------------

      添加以下路径

      1 -> 100 -> 400
      1 -> 200 -> 400
      1 -> 300 -> 400
      

      最后一个查询打印(再次)

      +-----------+
      | FRIEND_ID |
      +-----------+
      |         4 |
      |         5 |
      |         5 |
      |       400 |
      |       400 |
      |       400 |
      +-----------+
      

      最后,我得到了候选人:4,5,400

      确保distinct确实适用于主要目标

      SELECT DISTINCT F2.FRIEND_ID
      FROM FRIEND F1 INNER JOIN FRIEND F2 ON F1.FRIEND_ID = F2.PLAYER_ID
      WHERE F1.PLAYER_ID = 1
      AND F2.FRIEND_ID != 1
      AND F2.FRIEND_ID NOT IN (SELECT FRIEND_ID FROM FRIEND WHERE PLAYER_ID = 1);
      

      打印

      +-----------+
      | FRIEND_ID |
      +-----------+
      |         4 |
      |         5 |
      |       400 |
      +-----------+
      

      而且,现在,需要按相互排序进行排序。

      每个候选人都有共同朋友的数量。

      +-----------+
      | FRIEND_ID |
      +-----------+
      |         4 | 1 (1 -> 2 -> 4)
      |         5 | 2 (1 -> 2 -> 5, 1 -> 3 -> 5)
      |       400 | 3 (1 -> 100 -> 400, 1 -> 200 -> 400, 1 -> 300 -> 400)
      +-----------+
      

      如何通过这些共同的朋友来计算和排序?

      SELECT F2.FRIEND_ID, COUNT(*)
      FROM FRIEND F1 INNER JOIN FRIEND F2 ON F1.FRIEND_ID = F2.PLAYER_ID
      WHERE F1.PLAYER_ID = 1
      AND F2.FRIEND_ID != 1
      AND F2.FRIEND_ID NOT IN (SELECT FRIEND_ID FROM FRIEND WHERE PLAYER_ID = 1)
      GROUP BY F2.FRIEND_ID;
      

      打印

      +-----------+----------+
      | FRIEND_ID | COUNT(*) |
      +-----------+----------+
      |         4 |        1 |
      |         5 |        2 |
      |       400 |        3 |
      +-----------+----------+
      

      我明白了!

      SELECT F2.FRIEND_ID, COUNT(*) AS MFC
      FROM FRIEND F1 INNER JOIN FRIEND F2 ON F1.FRIEND_ID = F2.PLAYER_ID
      WHERE F1.PLAYER_ID = 1
      AND F2.FRIEND_ID != 1
      AND F2.FRIEND_ID NOT IN (SELECT FRIEND_ID FROM FRIEND WHERE PLAYER_ID = 1)
      GROUP BY F2.FRIEND_ID
      ORDER BY MFC DESC;
      

      打印

      +-----------+-----+
      | FRIEND_ID | MFC |
      +-----------+-----+
      |       400 |   3 |
      |         5 |   2 |
      |         4 |   1 |
      +-----------+-----+
      

      任何人都可以确认一下吗?该查询是否最佳?将它作为视图时会出现任何可能的性能问题吗?

      谢谢。

      更新---------------------------------------------- ----------------------------------------------

      我创建了一个视图

      CREATE VIEW FOLLOWABLE AS
          SELECT F1.PlAYER_ID, F2.FRIEND_ID AS FOLLOWABLE_ID, COUNT(*) AS MFC
          FROM FRIEND F1 INNER JOIN FRIEND F2 ON F1.FRIEND_ID = F2.PLAYER_ID
          WHERE F2.FRIEND_ID != F1.PLAYER_ID
          AND F2.FRIEND_ID NOT IN (SELECT FRIEND_ID FROM FRIEND WHERE PLAYER_ID = F1.PLAYER_ID)
          GROUP BY F2.FRIEND_ID
          ORDER BY MFC DESC;
      

      并经过测试。

      mysql> select * from FOLLOWABLE;
      +-----------+---------------+-----+
      | PlAYER_ID | FOLLOWABLE_ID | MFC |
      +-----------+---------------+-----+
      |         1 |           400 |   3 |
      |         1 |             5 |   2 |
      |         2 |           100 |   1 |
      |         2 |           200 |   1 |
      |         2 |           300 |   1 |
      |         1 |             4 |   1 |
      +-----------+---------------+-----+
      6 rows in set (0.01 sec)
      
      mysql> select * from FOLLOWABLE WHERE PLAYER_ID = 1;
      +-----------+---------------+-----+
      | PlAYER_ID | FOLLOWABLE_ID | MFC |
      +-----------+---------------+-----+
      |         1 |           400 |   3 |
      |         1 |             5 |   2 |
      |         1 |             4 |   1 |
      +-----------+---------------+-----+
      3 rows in set (0.00 sec)
      

1 个答案:

答案 0 :(得分:7)

使用它 的修改

SELECT `friend_id` AS `possible_friend_id`
FROM `friends`
WHERE `player_id` IN (        --selecting those who are known
    SELECT `friend_id`        --by freinds of #1
    FROM `friends`
    WHERE `player_id` = 1) 
AND `friend_id` NOT IN (      --but not those who are known by #1
    SELECT `friend_id`
    FROM `friends`
    WHERE `player_id` = 1)
AND NOT `friend_id` = 1       --and are not #1 himself
                              --if one is known by multiple people
                              --he'll be multiple time in the list
GROUP BY `possible_friend_id` --so we group
ORDER BY COUNT(*) DESC        --and order by amount of repeatings