Я беру список элементов из одной таблицы на основании того, что они включены в другую таблицу, например:
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, независимо от того, кто еще их оценил?


... 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)
Я немного уточнил ваш запрос, чтобы его было немного легче читать, и добавил подзапрос, чтобы отфильтровать все фрукты, оцененные пользователем 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. Тем не менее, спасибо за ответ.
Я читаю это иначе, чем Коуэн, и согласен с Ноем ...
Найдите все фрукты, где: - Пользователь 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
Да здесь ТАК скорость имеет значение :). Но вы написали весь запрос, так что я думаю, это тоже полезно.