2012-06-14 3 views
1

Я храню некоторые данные в MySQL и хочу отфильтровывать строки, соответствующие заданным критериям.Применить фильтр к объединенным таблицам с MySQL

Звучит просто, но это не так, поскольку есть некоторые критерии присоединения.

У меня есть следующие таблицы:

items : id, ... 
genres: id, name:varchar, item_id 

Каждый элемент имеет несколько жанров.

Запрос должен фильтровать из элементов, если хотя бы один жанр соответствует указанному имени жанра (или набору имен).

Например:

Item with id 1 has 3 genres 
    - genre name = 'foo' 
    - genre name = 'bar' 
    - genre name = 'baz' 

Пункт 1 не может быть частью результата, если данное имя жанр «бар», [ «бар», «Баз», «хуг»] и т.д.

Я попытался уйти в жанры по предметам и применил инструкцию WHERE с помощью «genres.name NOT IN (?)». ? это заданный набор жанровых имен.

Это (конечно) работает только для предметов с одним жанром. То же самое можно было бы достичь за счет нескольких условий WHERE: WHERE name <> 'a' И имя <> 'b' ...

Любые идеи о том, как правильно выполнить этот запрос?

Заранее благодарен!

+0

Не могли бы вы дать пример запроса, объяснить, что он делает, а затем объяснить, что вы хотите, чтобы он поступил иначе? –

+0

Я еще не понимаю таблицы ... поэтому у вас есть таблица 'items' и таблица' genres'. Почему в последней таблице есть столбец 'item_id'? Я ожидал увидеть таблицу ассоциаций «многие-ко-многим», которая связывает элементы с жанрами. –

+0

Почему вы хотите отфильтровать элементы, если по крайней мере один жанр соответствует определенному жанру? Где, как в первой строке вашей проблемы, вы написали противоположное. –

ответ

0

Вы можете использовать коррелированный подзапрос.
Пример:

SELECT id 
FROM items i 
WHERE NOT EXISTS (SELECT 1 
        FROM genres g 
        WHERE g.`name` = 'bar' 
        AND i.id = g.item_id); 

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

+0

Я думаю, это именно то, что я искал. Спасибо, Берни! – Peter

+0

Добро пожаловать. Счастливое кодирование. – bernie

1

Вы можете сделать что-то вроде этого (see sqlfiddle):

select i.name as item, g.name as genre 
from items as i 
left join genres as g 
    on i.id = g.item_id 
where i.id not in 
     (select distinct g2.item_id from genres as g2 
     where FIND_IN_SET(g2.name,'foo,bar')); 

И таким образом он работает, если вы хотите, чтобы проверить против нескольких названий жанров.

+2

Спасибо! Также взгляните на подход bernies. Решает проблему также. – Peter

0

Надеюсь, я понимаю, о чем вы просите, и я думаю, что MySQL и T-SQL будут подобны в этом отношении. Это примерные таблицы, которые я предположил, что вы использовали в своем примере.

Table1 
ID NAME 
1 Item 1 
2 Item 2 
3 Item 3 
4 Item 4 
5 Item 5 
6 Item 6 

Table2 
ID NAME ITEM_ID 
1 Genre 1 1 
2 Genre 2 1 
3 Genre 3 1 
4 Genre 1 2 
5 Genre 2 2 
6 Genre 3 2 
7 Genre 1 3 
8 Genre 2 3 
9 Genre 3 3 
10 Genre 1 4 

Вот SQL для фильтрации Out элементов, если они соответствуют одному из ваших критериев.

SELECT * 
FROM table1 a, table2 b 
WHERE a.id = b.item_id 
AND b.name NOT IN ('Genre 1', 'Genre 2') 

Результаты, полученные из запроса на таблицы образцов.

ID NAME ID_1 NAME_1 ITEM_ID 
1 Item 1 3 Genre 3 1 
2 Item 2 6 Genre 3 2 
3 Item 3 9 Genre 3 3 

Вы также можете отфильтровать вместо genre_id (table2.id). Если вы планируете использовать подзапрос вместо жестко закодированных значений, вы должны переключить «NOT IN» на «NOT EXISTS» и переписать часть «AND» соответственно, так как «NOT IN» не любит нулевые значения.

Этот запрос также предполагает, что каждый «Предмет» имеет по крайней мере 1 «Жанр». Я уверен, что вы можете играть с ним, если хотите, чтобы все «Элементы» были исключены из тех, которые соответствуют вашим критериям.

+0

Спасибо, но это не сработает, потому что нужно предоставить все жанровые имена элемента, чтобы получить элемент из списка. По крайней мере одно подходящее имя жанра должно выполнять эту работу. – Peter

0
select * from items i where not exists 
(select '' from genres gen where gen.item_id = i.id and gen.name in ('foo','pop')) 

Более короткий и простой запрос. Внутренний запрос проверяет имя жанра в списке ключевых слов, если строка не возвращается, выбирается текущая строка внешнего запроса.
Проверить выход at here.

Благодаря Kyra я заимствовал его схему из скрипта sql.

+0

Спасибо. Отвечает на мой вопрос! – Peter