SQL: Как сделать выбор на основе категорий? - PullRequest
3 голосов
/ 23 июня 2010

Есть две таблицы, categories и books, и я хочу выбрать все книги на основе указанных категорий.

Таблица категорий:

cat_id | book_id
----------------
1      | 1
2      | 1
3      | 1
3      | 2

Таблица книг:

id  | name
----------------
1   | abc
2   | def

Я пробовал SELECT * FROM categories WHERE cat_id IN(1,3), но затем он возвращает книги, содержащие хотя бы одну из указанных категорий.Мне бы хотелось, чтобы он возвращал только книги, содержащие ВСЕ категории, поэтому он должен возвращать только все (или одну) строки, где book_id = 1, поскольку это единственная книга со всеми заданными категориями.

Ответы [ 6 ]

3 голосов
/ 23 июня 2010

Попробуйте:

select book_id
from categories
group by book_id
having sum( ( cat_id in (1,3) )::int ) = 2

Или, если вы намереваетесь передать массив в postgres из языка, который поддерживает передачу массива непосредственно в него (например: http://fxjr.blogspot.com/2009/05/npgsql-tips-using-in-queries-with.html),, используйте это:

select book_id
from categories
group by book_id
having sum( ( cat_id = ANY(ARRAY[1,3]) )::int ) = 2

Если вы хотите получить название книги:

select categories.book_id, books.name
from categories
join books on books.id = categories.book_id
group by categories.book_id
    ,books.name
having sum( ( categories.cat_id in (1,3) )::int ) = 2

@ Эван Кэрролл, исправляя запрос:

Способ ANSI SQL:

select categories.book_id, books.name
from categories
join books on books.id = categories.book_id
group by categories.book_id
    ,books.name
having count(case when categories.cat_id in (1,3) then 1 end) = 2

без названия книги:

select book_id
from categories
group by book_id
having count( case when cat_id in (1,3) then 1 end ) = 2

В чем преимущество включения условия и его значения счетчика в одном и том же предложении (т. Е. having) по сравнению с раздельным помещением условия в предложении where и его количеством в предложении having? ...

select book_id
from categories
where category_id in (1,3)
group by book_id
having count(*) = 2

... Если мы включим в условие having и условие, и его значение счетчика, мы можем упростить запрос, скажем, списка всех книг с категориями 1 и 3 или с категориями 2 и 3 и 4 . Перспективный FTW! Плюс тестирование объединенных категорий и их количество рядом друг с другом, плюс фактор с точки зрения читабельности.

Чтобы упростить такой запрос:

select book_id
from categories
group by book_id
having 
    count( case when cat_id in (1,3) then 1 end ) = 2 
    or count( case when cat_id in (2,3,4) then 1 end ) = 3

Для достижения производительности (иногда достижения как производительности, так и читабельности; плохо сочетаются), должен продублировать тестирование элементов, имеющих предложение, к предложению where:

select book_id
from categories
where cat_id in (1,2,3,4)
group by book_id
having 
    count( case when cat_id in (1,3) then 1 end ) = 2 
    or count( case when cat_id in (2,3,4) then 1 end ) = 3

[EDIT]

Кстати, вот идиоматический MySQL:

select book_id
from categories
group by book_id
having sum( cat_id in (1,3) ) = 2
3 голосов
/ 23 июня 2010

На самом деле вы получаете более одной записи на книгу. Если n из n категорий назначено книге, вы получите n записей для книги. Таким образом, вы можете сгруппировать свой запрос и выбрать только тех, кто имеет n хитов:

SELECT T.cat_id, count(*) hits FROM
(
    SELECT * FROM categories WHERE cat_id IN(1,3)
) T
GROUP BY T.cat_id
HAVING hits = 2
1 голос
/ 23 июня 2010

Еще один альтернативный метод:

SELECT book_id FROM categories WHERE cat_id = 1 
INTERSECT 
SELECT book_id FROM categories WHERE cat_id = 3;

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

0 голосов
/ 23 июня 2010
SELECT * FROM 
(
 SELECT b.id, count(c.cat_id) as cat_count
 FROM books AS b
 JOIN cats AS c
   ON ( b.id = c.book_id )
 GROUP BY b.id
) AS t
WHERE t.cat_count = ( SELECT DISTINCT count(cat_id) FROM cat );

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

0 голосов
/ 23 июня 2010

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

SELECT books.*
FROM books
     JOIN categories cat1 ON cat1.book_id = books.book_id
     JOIN categories cat3 ON cat3.book_id = books.book_id
WHERE cat1.cat_id = 1
      AND cat3.cat_id = 3

Или вы делаете это эквивалентно, используя WHERE EXISTS (semi join), если вам не нравится добавлять внутренние объединения.

0 голосов
/ 23 июня 2010

Попробуйте это:

SELECT * FROM books WHERE id IN 
(SELECT book_id
FROM categories
GROUP BY book_id 
HAVING COUNT(distinct cat_id)  = (select count(distinct cat_id) from categories))

Отредактировано: я редактировал запрос, чтобы он возвращал книги, содержащие ВСЕ категории, как указано в вопросе

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...