【问题标题】:SQL query to exclude items on the basis of one value基于一个值排除项目的 SQL 查询
【发布时间】:2009-01-08 21:46:26
【问题描述】:

我正在从一个表中提取项目列表,因为它们包含在另一个表中,如下所示:

select fruit.id, fruit.name from fruit, fruit_rating where fruit_rating.fruit_id=fruit.id group by fruit.name;

这很好用——它基本上会生成一个列表,列出所有已被某人评分的水果。但是现在,我想排除所有已被某个特定用户评分的水果,所以我尝试了这个:

select fruit.id, fruit.name from fruit, fruit_rating where fruit_rating.fruit_id=fruit.id and fruit_rating.user_id != 10 group by fruit.name;

没关系,但不完全正确。它显示了除 10 以外的人评价过的所有水果,但如果用户 1 和 10 都评价了同一个水果,它仍然显示那个。谁能告诉我如何构建一个查询,只显示用户 10 未评级的水果,而不管其他人对它们进行了评级吗?

【问题讨论】:

    标签: sql mysql


    【解决方案1】:
    ... WHERE fruit_rating.fruit_id=fruit.id 
          and fruit.id not in 
              (select fruit_rating.fruit_id 
                 from fruit_rating 
                where fruit_rating.user_id = 10)
    

    【讨论】:

    • 是的,速度很重要:)。但是你写了整个查询,所以我想它也很有用。
    【解决方案2】:

    我与 Cowan 的读法不同,并且同意诺亚的观点……

    找到所有水果: - 用户 10 没有评价它 - 至少有其他用户评价它

    但是,根据我的经验,使用 NOT IN 可能会很慢。因此,我通常更喜欢使用与 Cowan 相同的方式使用 LEFT JOIN 进行过滤。这里有几个不同的选项,虽然我没有时间在大型数据集上测试性能...

    SELECT
       [f].id,
       [f].name
    FROM
       fruit           AS [f]
    INNER JOIN
       fruit_rating    AS [fr]
          ON [fr].fruit_id = [f].id
    GROUP BY
       [f].id,
       [f].name
    HAVING
       SUM(CASE WHEN [fr_exclude].user_id = 10 THEN 1 ELSE 0 END) = 0
    


    SELECT
       [f].id,
       [f].name
    FROM
       fruit           AS [f]
    INNER JOIN
       fruit_rating    AS [fr]
          ON [fr].fruit_id = [f].id
    LEFT JOIN
       fruit_rating    AS [fr_exclude]
          ON [fr_exclude].fruit_id = [fr].fruit_id
          AND [fr_exclude].user_id = 10
    GROUP BY
       [f].id,
       [f].name
    HAVING
       MAX([fr_exclude].user_id) IS NULL
    


    由于这只适用于一个用户,我还会考虑制作一个“要排除的用户”表并在其上使用 LEFT JOIN...

    SELECT
       [f].id,
       [f].name
    FROM
       fruit           AS [f]
    INNER JOIN
       fruit_rating    AS [fr]
          ON [fr].fruit_id = [f].id
    LEFT JOIN
       excluded_users  AS [ex]
          AND [ex].user_id = [fr].user_id
    GROUP BY
       [f].id,
       [f].name
    HAVING
       MAX([ex].user_id) IS NULL
    


    或者更冗长的东西,但我怀疑在具有适当索引的大型数据集上是最快的......

    SELECT
       [f].id,
       [f].name
    FROM
       fruit           [f]
    INNER JOIN
    (
       SELECT
          fruit_id
       FROM
          fruit_rating
       GROUP BY
          fruit_id
    )
       AS [rated]
          ON [rated].fruit_id = [f].id
    LEFT JOIN
    (
       SELECT
          [fr].fruit_id
       FROM
          fruit_rating    AS [fr]
       INNER JOIN
          excluded_users  AS [ex]
             ON [ex].user_id = [fr].user_id
       GROUP BY
          [fr].fruit_id
    )
       AS [excluded]
          ON [rated].fruit_id = [excluded].fruit_id
    WHERE
       [excluded].fruit_id IS NULL
    GROUP BY
       [f].id,
       [f].name
    

    【讨论】:

      【解决方案3】:

      我稍微改进了您的查询以使其更易于阅读,并添加了一个子查询以过滤掉用户 10 已评分的所有水果

      select f.id, f.name 
      from fruit f
      inner join fruit_rating fr on
       fr.fruit_id = f.id 
      where f.id not in (
          select id
          from fruit_rating
          where [user_id] = 10) 
      group by fruit.name;
      

      【讨论】:

        【解决方案4】:

        有一件事我不是 100% 清楚:你想要 所有 没有被用户 10 评价过的水果,还是只需要其他人评价过但没有评价过的水果?由用户 10?例如应该包括没有评级的水果吗?

        认为你想要所有的水果(包括未分级的),在这种情况下,诺亚和布朗斯通先生的答案并不完全符合你的要求。如果您删除fruit_rating 的内部连接,以及现在不需要的group by,他们的将包括未评级的水果。另一种避免子选择的方法是

        select f.id, f.name 
        from fruit f
        left join fruit_rating fr on
          (f.id = fr.fruit_id)
          and (fr.user_id = 10)
        where
          (fr.user_id is null)
        

        也就是说,只为用户 10 对水果评级进行左连接(如果您愿意,可以选择连接),然后只返回未找到匹配项的行。

        【讨论】:

        • 对不起:我想我最初的问题措辞有点松散。我实际上是在寻找 NOah 和 Brownstone 先生的建议——即,我只想要用户 10 以外的人评价过的水果。不过,无论如何,感谢您的回复。
        猜你喜欢
        • 1970-01-01
        • 2019-03-06
        • 2018-03-10
        • 1970-01-01
        • 2016-05-15
        • 2014-01-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多