Try:
select book_id
from categories
group by book_id
having sum( ( cat_id in (1,3) )::int ) = 2
Or if you intend to pass an array to postgres from language that supports passing array directly to it(like this: http://fxjr.blogspot.com/2009/05/npgsql-tips-using-in-queries-with.html), use this:
select book_id
from categories
group by book_id
having sum( ( cat_id = ANY(ARRAY[1,3]) )::int ) = 2
If you want to get the book name:
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
@Evan Carroll, amending the query:
ANSI SQL way:
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
Sans the book name:
select book_id
from categories
group by book_id
having count( case when cat_id in (1,3) then 1 end ) = 2
What's the advantage of inlining the condition and its count value in the same clause(i.e. having
) as opposed to separately putting the condition in where
clause and its count in having
clause?...
select book_id
from categories
where category_id in (1,3)
group by book_id
having count(*) = 2
...If we inline both the condition and its count value in having
clause, we can facilitate an inquiry of let's say list all books with categories of 1 and 3, or with categories of 2 and 3 and 4. Future-proofing FTW! Plus the testing of combined categories and its count are next to each other, plus factor in terms of readability.
To facilitate that kind of query:
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
To achieve performance(sometimes, achieving both performance and readability; don't mix well), must duplicate the testing of elements of having clause to where clause:
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]
BTW, here's the idiomatic MySQL:
select book_id
from categories
group by book_id
having sum( cat_id in (1,3) ) = 2