SQL:如何根据类别进行选择?

时间:2010-06-23 14:09:27

标签: sql postgresql

有两个表格categoriesbooks,我想根据给定的类别选择所有图书。

分类表:

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 个答案:

答案 0 :(得分:3)

每本书实际上有多个条目。如果将 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 :(得分:3)

尝试:

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

@Evan Carroll,修改查询:

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

为了达到性能(有时候,实现性能和可读性;不要混合得好),必须将having子句元素的测试复制到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

[编辑]

BTW,这是惯用的MySQL:

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

答案 2 :(得分:1)

另一种替代方法:

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

如果您要匹配两个以上的类别,则可以继续链接INTERSECT。

答案 3 :(得分:0)

加入您需要的每个类别:

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(半联接)等效地执行此操作。

答案 4 :(得分:0)

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 );

这假定一本书不能在同一类别中两次。这将选择任一类别中的所有书籍,计算类别,并确保类别计数是最大类别数。

答案 5 :(得分:-1)

试试这个:

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))

编辑:我编辑了查询,以便返回包含问题

中所述的所有类别的书籍