SQL-запрос для исключения элементов на основе одного значения

Я беру список элементов из одной таблицы на основании того, что они включены в другую таблицу, например:

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, независимо от того, кто еще их оценил?

ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
3
0
17 743
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий
... 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)

Да здесь ТАК скорость имеет значение :). Но вы написали весь запрос, так что я думаю, это тоже полезно.

Rockcoder 09.01.2009 01:06

Я немного уточнил ваш запрос, чтобы его было немного легче читать, и добавил подзапрос, чтобы отфильтровать все фрукты, оцененные пользователем 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;

Одна вещь, которая мне не совсем ясна: хотите ли вы фрукт все, который не был оценен пользователем 10, или просто фрукт, который был оценен другими людьми, но не пользователем 10? например следует ли включать фрукты, у которых нет оценок?

Я считать вы хотите все фрукты (включая безрейтинговые), и в этом случае ответы Ноя и мистера Браунстоуна не совсем то, что вам нужно. Если вы удалите внутреннее соединение с fruit_rating и ненужную группу 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, а затем возвращайте только строки, в которых совпадение НЕ найдено.

Извините: я предполагаю, что мой первоначальный вопрос был сформулирован немного неточно. На самом деле я искал то, что предлагали Ной и мистер Браунстоун - то есть, я хотел только фрукты, которые были оценены людьми, отличными от пользователя 10. Тем не менее, спасибо за ответ.

Ben 09.01.2009 02:01

Я читаю это иначе, чем Коуэн, и согласен с Ноем ...

Найдите все фрукты, где: - Пользователь 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

Другие вопросы по теме