Проблема SQL многие ко многим запросам - PullRequest
5 голосов
/ 22 мая 2009

У меня есть три таблицы: видео, видео_категории и категории.

Таблицы выглядят так:

videos: video_id, title, etc...
videos_categories: video_id, category_id
categories: category_id, name, etc...

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

Я закончил с этим:

SELECT * FROM videos WHERE video_id IN (
    SELECT c1.video_id FROM videos_categories AS c1
    JOIN c2.videos_categories AS c2
    ON c1.video_id = c2.video_id
    WHERE c1.category_id = 1 AND c2.category_id = 2
)

Но для каждой категории, которую я добавляю в мультиселект, я должен добавить соединение к своему внутреннему выбору:

SELECT * FROM videos WHERE video_id IN (
    SELECT c1.video_id FROM videos_categories AS c1
    JOIN videos_categories AS c2
    ON c1.video_id = c2.video_id
    JOIN videos_categories AS c3
    ON c2.video_id = c3.video_id
    WHERE c1.category_id = 1 AND c2.category_id = 2 AND c3.category_id = 3
)

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

Ответы [ 3 ]

6 голосов
/ 22 мая 2009

, если это первичный ключ:

 videos_categories: video_id, category_id

тогда GROUP BY и HAVING должны работать, попробуйте это:

SELECT
    * 
    FROM videos 
    WHERE video_id IN (SELECT 
                           video_id
                           FROM videos_categories
                           WHERE category_id IN (1,2,3)
                           GROUP BY video_id
                           HAVING COUNT(video_id)=3
                      )
0 голосов
/ 22 мая 2009

Звуки, похожие на SQL-поиск строк, которые содержат несколько критериев

Чтобы избежать необходимости повторного объединения для каждой категории (и, следовательно, изменения структуры запроса), вы можете поместить категории во временную таблицу, а затем объединить ее.

CREATE TEMPORARY TABLE query_categories(category_id int);
INSERT INTO query_categories(category_id) VALUES(1);
INSERT INTO query_categories(category_id) VALUES(2);
INSERT INTO query_categories(category_id) VALUES(3);

SELECT * FROM videos v WHERE video_id IN (
  SELECT video_id FROM video_categories vc JOIN query_categories q ON vc.category_id = qc.category_id
  GROUP BY video_id
  HAVING COUNT(*) = 3
)

Хотя это, конечно, по-своему некрасиво. Вы можете пропустить временную таблицу и просто сказать «category_id IN (...)» в подзапросе.

0 голосов
/ 22 мая 2009

Вот решение FOR XML PATH:

--Sample data
CREATE TABLE Video
    (
    VideoID int,
    VideoName varchar(50)
    )

CREATE TABLE Videos_Categories
    (
    VideoID int,
    CategoryID int
    )

INSERT  Video(VideoID, VideoName)
SELECT  1, 'Indiana Jones'
UNION ALL
SELECT  2, 'Star Trek'

INSERT Videos_Categories(VideoID, CategoryID)
SELECT 1, 1
UNION ALL
SELECT 1, 2
UNION ALL
SELECT 1, 3
UNION ALL
SELECT 2, 1
GO

--The query
;WITH   GroupedVideos
AS
(
SELECT  v.*,
        SUBSTRING(
                    (SELECT  (', ') + CAST(vc.CategoryID AS varchar(20))
                    FROM     Videos_Categories AS vc
                    WHERE    vc.VideoID = v.VideoID
                    AND      vc.CategoryID IN (1,2)
                    ORDER BY vc.CategoryID
                    FOR XML PATH('')), 3, 2000) AS CatList
FROM    Video AS v
)

SELECT  *
FROM    GroupedVideos
WHERE   CatList = '1, 2'

(игнорировать все ниже - я неправильно понял вопрос)

Попробуйте

WHERE c1.category_id IN (1,2,3)

или

...
FROM videos v
JOIN Vedeos_categories vc ON v.video_id = vc.video_id
WHERE vc.category_id IN (1,2,3)

Несколько объединений вообще не нужны.

Редактировать: поместить решения в контекст (я понимаю, что это не очевидно):

SELECT * 
FROM videos 
WHERE video_id IN 
(    SELECT c1.video_id 
FROM videos_categories AS c1
WHERE c1.category_id = IN (1,2,3))

или

SELECT *
FROM videos v
JOIN Vedeos_categories vc ON v.video_id = vc.video_id
WHERE vc.category_id IN (1,2,3)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...