SQL查询以基于一个值排除项目

时间:2009-01-08 21:46:26

标签: sql mysql

我从一个表中提取项目列表,基于它们被包含在另一个表中,如下所示:

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未评级的水果,无论其他人对它们进行了评分吗?

4 个答案:

答案 0 :(得分:6)

... 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)

答案 1 :(得分:4)

我和Cowan的看法不一样,并且同意诺亚......

查找所有水果: - 用户10没有给它评分 - 至少有一个其他用户评价了它

但是,根据我的经验,使用NOT IN可能会非常慢。因此,我通常更喜欢使用LEFT JOIN以与Cowan相同的方式进行过滤。以下是一些不同的选项,但我没有时间测试大型数据集的性能......

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

答案 2 :(得分: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;

答案 3 :(得分:1)

对我来说,有一件事情并不是100%明确:你想要所有水果没有被用户10评级,或只是水果已被其他人评定但不是用户10?例如应该包括没有评级的水果吗?

我想你想要所有的水果(包括未评级的),在这种情况下,诺亚和布朗斯通先生的答案并不是你所追求的。如果你删除内部联接到fruit_rating,以及现在不必要的组,他们将包括未分级的水果。另一种避免子选择的方法是

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进行左连接(可选连接,如果你愿意)到水果评级,然后只返回未找到匹配的行。