【问题标题】:mySql - find all not friends of a usermySql - 查找所有不是用户的朋友
【发布时间】:2015-05-08 04:24:16
【问题描述】:

我正在创建一个社交网络,并且我正在尝试编写一个 mySql 查询来查找不是特定用户朋友的所有用户。 有一张表 - USERS,有 id_user、name 和 email。

USERS:
user_id , name , email..
1         Dan     a@a.com
2         Ron     b@b.com
3         James   c@c.com
4         Joe     d@d.com
5         Max     c@c.com

以及显示关系的 FRIENDS 表(status=2 表示两者是朋友):

FRIENDS:
user_id , friend_id , status
1             2         2
3             1         2
4             3         2 

什么是 mySql 查询,它给出了不是 user_id =1 朋友的 user_id(来自 USERS)的列表? (用户 4 和 5)。

非常感谢,

【问题讨论】:

    标签: mysql social


    【解决方案1】:

    反连接模式就足够了。

    看起来“a isafriend of b”在friends 表中可以用一行表示,(a,b,2)(b,a,2)。 (这对我来说并不完全清楚,但我现在假设这是真的。)

    这个查询只是第一次切割。为了在朋友表中双向检查朋友,我将在连接谓词中使用OR 条件。

    这实际上是从users 获取所有行,以及从friends 获取“匹配”行。 “技巧”是WHERE 子句中的谓词,它消除了在朋友中找到“匹配”的所有行,只留下users 中没有“匹配”的那些行。

    SELECT u.user_id
         , u.name
      FROM users u
      LEFT
      JOIN friends f
        ON ( f.user_id   = u.user_id AND f.friend_id = ? AND f.status = 2 )
        OR ( f.friend_id = u.user_id AND f.user_id   = ? AND f.status = 2 )
     WHERE f.user_id   IS NULL
        OR f.friend_id IS NULL
    

    不幸的是,MySQL 优化器通常不会太优雅地处理这些OR 条件。作为重新编写的另一个剪辑,我们可以尝试将与朋友的联接拆分为两个左联接。

    我认为这是等价的:

    SELECT u.user_id
         , u.name
      FROM users u
      LEFT
      JOIN friends f
        ON ( f.user_id   = u.user_id AND f.friend_id = ? AND f.status = 2 )
      LEFT
      JOIN friends g
        OR ( g.friend_id = u.user_id AND g.user_id   = ? AND g.status = 2 )
     WHERE f.user_id   IS NULL 
       AND g.friend_id IS NULL
    

    (我还没有测试第二个查询...目标是做同样的事情。来自users 的所有行,以及来自friends 的匹配行,然后过滤掉所有具有match.

    编辑

    我认为我们还需要从“特定用户”users 中过滤掉该行。上面的查询会将用户自己返回为他自己的“不是朋友”。为此,我们可以添加到WHERE 子句

     AND u.user_id <> ?
    

    对于第一个查询,我们需要注意 ORAND 布尔运算符之间的优先顺序...我认为我们需要将两个 OR'd 条件包装在括号中的第一个查询。


    跟进

    还有一些其他查询模式可以返回等效结果。作为两个例子,我们可以使用NOT IN (subquery) 形式的谓词,或者NOT EXISTS (subquery) 形式的谓词。

    使用“NOT IN(子查询)”

    使用这种形式,请注意子查询不会返回 NULL 值。如果列表中有 NULL 值,谓词将返回 TRUE。

    SELECT u.user_id
         , u.name
      FROM users u
     WHERE u.user_id NOT IN ( SELECT f.user_id
                                FROM friends f
                               WHERE f.user_id IS NOT NULL
                                 AND f.status = 2
                                 AND f.friend_id = ?
                            )
       AND u.user_id NOT IN ( SELECT f.friend_id
                                FROM friends f
                               WHERE f.friend_id IS NOT NULL
                                 AND f.status = 2
                                 AND f.user_id = ?
                            )
       AND u.user_id <> ?
    

    使用“不存在(子查询)”

    SELECT u.user_id
         , u.name
      FROM users u
     WHERE NOT EXISTS ( SELECT 1 
                          FROM friends f
                         WHERE f.user_id = u.user_id
                           AND f.status = 2
                           AND f.friend_id = ?
                      )
       AND NOT EXISTS ( SELECT 1
                          FROM friends g
                         WHERE g.friend_id = u.user_id
                           AND g.status = 2
                           AND g.user_id = ?
                      )
       AND u.user_id <> ?
    

    【讨论】:

    • @Strawberry:我不得不在某个地方停下来。从“个人满意度”的角度来看,我希望friends 表中的行数增加一倍,在该表中同时包含(a,b)(b,a)。我可以使用内联视图、两个查询与 UNION ALL 集合运算符相结合,以返回该集合。对于“特定用户”,我会使用适当的谓词。但是,我们可以通过有条件地“交换”ab 来获得相同的结果,而无需使用 UNION ALL。我不得不在某个地方停下来。
    猜你喜欢
    • 2015-01-15
    • 1970-01-01
    • 1970-01-01
    • 2014-01-19
    • 2018-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多