Вопрос

У меня есть две таблицы:«фильмы» и «пользователи».Между ними существует связь n:m, описывающая, какие фильмы смотрел пользователь.Это описано с помощью таблицы «Вид», теперь я хочу узнать для данного пользователя, всех фильмов, которых он не видел.Мое текущее решение выглядит следующим образом:

SELECT *
FROM movies 
WHERE movies.id NOT IN (
     SELECT seen.movie_id 
     FROM seen 
     WHERE seen.user_id=123
)

Это отлично работает, но, похоже, не очень хорошо масштабируется.Есть ли лучший подход к этому?

Это было полезно?

Решение

Вот типичный способ выполнения этого запроса без использования показанного вами метода подзапроса.Это может удовлетворить запрос @Godeke на поиск решения на основе объединения.

SELECT * 
FROM movies m
 LEFT OUTER JOIN seen s
 ON (m.id = s.movie_id AND s.user_id = 123)
WHERE s.movie_id IS NULL;

Однако в базах данных большинства марок это решение может работать хуже, чем решение с подзапросом.Лучше всего использовать EXPLAIN для анализа обоих запросов, чтобы увидеть, какой из них будет лучше с учетом вашей схемы и данных.

Вот еще один вариант решения подзапроса:

SELECT * 
FROM movies m
WHERE NOT EXISTS (SELECT * FROM seen s 
                  WHERE s.movie_id = m.id 
                    AND s.user_id=123);

Это коррелированный подзапрос, который необходимо оценивать для каждой строки внешнего запроса.Обычно это дорого, и исходный пример запроса лучше.С другой стороны, в MySQL "NOT EXISTS"часто лучше, чем"column NOT IN (...)"

Опять же, чтобы быть уверенным, вы должны протестировать каждое решение и сравнить результаты. Выбор любого решения без измерения производительности — пустая трата времени.

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

Ваш запрос не только работает, но и является правильным подходом к заявленной проблеме.Возможно, вы сможете найти другой подход к проблеме?Простой LIMIT для вашего внешнего выбора должен быть очень быстрым, например, даже для больших таблиц.

Видна ваша таблица соединений, так что да, это похоже на правильное решение.Вы фактически «вычитаете» набор идентификаторов фильмов в SEEN (для пользователя) из общего количества в MOVIES, в результате чего для этого пользователя появляются невидимые фильмы.

Это называется «негативным соединением», и, к сожалению, NOT IN или NOT EXISTS — лучшие варианты.(Мне бы хотелось увидеть синтаксис отрицательного соединения, похожий на соединения INNER/OUTER/LEFT/RIGHT, но где предложение ON могло бы быть оператором вычитания).

Решение @Bill без подзапроса должно работать, хотя, как он отметил, было бы неплохо проверить ваше решение на производительность в обоих направлениях.Я подозреваю, что подзапрос или нет, но весь индекс SEEN.ID (и, конечно, весь индекс MOVIE.ID) будет оцениваться в обоих направлениях:это будет зависеть от того, как оптимизатор с этим справится.

Если ваша СУБД поддерживает растровые индексы, вы можете попробовать их.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top