【问题标题】:MySQL - NOT IN produce unwanted resultMySQL - NOT IN 产生不需要的结果
【发布时间】:2013-01-06 00:22:16
【问题描述】:

我有以下表格:

user
+-----------------------------------------------+
|  user_id    |  username   | Password | ...    |
+-----------------------------------------------+
|     1       |     a       |  ***     | ...    |
+-----------------------------------------------+
|     2       |     b       |  ***     | ...    |
+-----------------------------------------------+
|     3       |     c       |  ***     | ...    |
+-----------------------------------------------+
|     4       |     d       |  ***     | ...    |
+-----------------------------------------------+
|     5       |     e       |  ***     | ...    |
+-----------------------------------------------+

friends
+-----------------------------------------------+
|  f_id    |  user_id   | friend_id | ...       |
+-----------------------------------------------+
|     1    |     4      |  2        | ...       |
+-----------------------------------------------+
|     2    |     4      |  1        | ...       |
+-----------------------------------------------+
|     3    |     4      |  5        | ...       |
+-----------------------------------------------+
|     4    |     4      |  3        | ...       |
+-----------------------------------------------+

我想获取所有可以添加为好友的用户(在这种情况下,1user_id 将有 3 个以上好友要添加 (2, 3, 5)。但是,通过使用以下 @987654325 @我只有1个用户(4)可以添加:

$sql = "SELECT * FROM user WHERE user.user_id NOT IN 
(SELECT friends.friend_id FROM friends) AND 
user.user_id <> $_SESSION['id']." ORDER BY RAND() LIMIT 5";

但是当我以用户 4 登录时,这很有效,没有可添加的用户。这对我来说有点棘手。任何想法将不胜感激。

谢谢

【问题讨论】:

  • 您的嵌套查询中不需要WHERE 子句吗?
  • 我也试过了,还是不行。我试过WHERE friends.friend_id &lt;&gt; $_SESSION['id'] 还是会是别的?
  • 否 - WHERE friends.user_id = $_SESSION['id'] - 您想找到 此用户 还不是朋友的人,对吗?所以你想说NOT IN说用户是朋友的人列表。
  • +1 @tepkenvannkorn 的问题,很有趣

标签: php mysql join not-exists notin


【解决方案1】:

编辑:

这个怎么样: (在 else 子句中使用无效的 id 或与登录用户相同的 id)

select * from users where id not in
(
   select (
            case
              when uid = 1 then id
              when fid = 1 then uid
             else 0
            end
          ) from friends where uid = 1 or fid = 1
) and id != 1 order by rand() limit 5;

http://sqlfiddle.com/#!2/12de1/62


解决此问题的另一种方法(但可能不是最佳解决方案):

如果用户拥有的朋友数少于系统中的用户总数,则与两个表之间的完全联接相比,下面的联合查询可能不是一个昂贵的查询。

另外不要忘记在friends 表的uidfid 列上添加索引。

select * from users where id not in 
(
     select id from friends where uid = 1
     union
     select uid from friends where fid = 1
) and id != 1 order by rand() limit 5;

http://sqlfiddle.com/#!2/12de1/40

【讨论】:

  • 第二个选项非常简单。感谢@Manu
【解决方案2】:
SELECT u1.id u1_id
     , u2.id u2_id
  FROM users u1
  JOIN users u2
    ON u2.id <> u1.id
  LEFT
  JOIN friends f
    ON (f.fid = u1.id AND f.uid = u2.id)
    OR (f.fid = u2.id AND f.uid = u1.id)
 WHERE f.id IS NULL;

【讨论】:

    【解决方案3】:

    这个怎么样? null 显示用户 4 不应将用户 4 添加为朋友的位置。事实上,您可以通过 And FID is not null 将其过滤掉。如果您想根据您的session id select to be added friends 特定用户,那么您也可以在另一种情况下指定它:)

    更新添加另一个加入,以确保名称显示给朋友,而不是他们的用户。以及不再存在4

    查询:

    select x.id, a.name, x.fid
    from users a
    join (
    select u.id, u.name, f.fid
    from users u
    left join friends f
    on u.id <> f.fid
    and u.id <> f.uid) x
    on x.fid = a.id
    ;
    

    结果:

    | ID | NAME | FID |
    -------------------
    |  1 |    b |   2 |
    |  1 |    e |   5 |
    |  1 |    c |   3 |
    |  2 |    a |   1 |
    |  2 |    e |   5 |
    |  2 |    c |   3 |
    |  3 |    b |   2 |
    |  3 |    a |   1 |
    |  3 |    e |   5 |
    |  5 |    b |   2 |
    |  5 |    a |   1 |
    |  5 |    c |   3 |
    

    对于特定用户,例如1

    SQLFIDDLE DEMO USER 1

    select x.id, x.fid, a.name
    from users a
    join 
    (select u.id, f.fid
    from users u
    inner join friends f
    on u.id <> f.fid
    and u.id <> f.uid
    and u.id = 1)x
    on x.fid = a.id
    ;
    
    | ID | FID | NAME |
    -------------------
    |  1 |   2 |    b |
    |  1 |   3 |    c |
    |  1 |   5 |    e |
    

    【讨论】:

    • @tepkenvannkorn 试试这个并发表评论:)
    • 我刚刚发布了一个类似的答案。这似乎方式比我的好! >.>
    • @RocketHazmat 很高兴看到我为最后一个查询提供了错误的链接。 :D
    • 不要在第二个示例中使用子查询,您可以再次JOIN users 表来获取用户名。 sqlfiddle.com/#!2/12de1/25
    • OP 可以检查explain plan 并决定;) sqlfiddle.com/#!2/12de1/33。我只是将两个查询保留在原始子查询方法中以保持一致性。
    【解决方案4】:

    您可以尝试JOIN friends 表,而不是使用带有子查询的NOT IN

    JOINed 了两次。一次1user_id,一次1friend_id。然后我得到了所有不是1 朋友或1 的用户。

    SELECT user.user_id,user.username
    
    FROM user
    
    LEFT JOIN friends AS fA ON fA.user_id = 1
    LEFT JOIN friends AS fB ON fB.friend_id = 1
    
    WHERE user.user_id != 1
    AND NOT (user.user_id <=> fB.user_id)
    AND NOT (user.user_id <=> fA.friend_id)
    

    演示:http://sqlfiddle.com/#!2/1a7ae/30

    【讨论】:

      【解决方案5】:

      您的 SQL 查询所做的是:获取 id 不等于 ANY friend_id 的所有用户,而不是当前用户(我假设)。

      您想要的是检查朋友表中的朋友ID和用户ID对是否存在记录,例如:$sql =

      "SELECT * FROM user WHERE user.user_id NOT IN 
      (SELECT friends.friend_id FROM friends WHERE friend.user_id = user.user_id) AND 
      user.user_id <> $_SESSION['id']." ORDER BY RAND() LIMIT 5";
      

      【讨论】:

        【解决方案6】:

        您在子查询中缺少 where 子句,以将其限制为您排除的当前用户朋友。

        $sql = "select * from user u 
            where u.user_id not in 
            (select f.friend_id from friends f where f.user_id = ".$_SESSION['id'].")
            and u.user_id <> ".$_SESSION['id']." ORDER BY RAND() LIMIT 5";
        

        我还建议不要使用字符串连接来执行此操作。 PDO 会更安全,不会有 SQL 注入的风险。

        http://php.net/manual/en/ref.pdo-mysql.php

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-08-11
          • 1970-01-01
          • 1970-01-01
          • 2022-01-18
          • 1970-01-01
          • 2019-02-18
          • 2019-05-12
          • 2020-04-20
          相关资源
          最近更新 更多