2010-06-23 33 views
3

有兩個表格,categoriesbooks,我想根據給定的類別選擇所有書籍。SQL:如何根據類別進行選擇?

分類表:

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的所有(或一個)行,因爲它是唯一具有所有給定類別的書籍。

回答

3

嘗試:

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

或者,如果你打算從支持直接傳遞數組給它(這樣的:http://fxjr.blogspot.com/2009/05/npgsql-tips-using-in-queries-with.html)語言傳遞一個數組Postgres的,使用這樣的:

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子句,其中的元素測試條款:

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 

[編輯]

順便說一句,這裏的慣用MySQL的:

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

這看起來相當尷尬,而且是錯誤的。sum用於添加參數,'count()'用於對行進行計數。看到我的答案更容易做到這一點。 – 2010-06-23 14:52:58

+0

之前你說這是錯的,這是一個習慣性的postgres。如果我使用mysql,我會這樣做:'sum(categories.cat_id in(1,3))',因爲在mysql中,布爾和整數是相同的,它們在幕後只有1和0 ,所以不需要更多的鑄造。對於postgresql,我們只需要將布爾值轉換爲整數就可以按照預期工作。好的,爲你我會使它符合ANSI SQL。編輯即將到來 – 2010-06-23 15:04:23

3

實際上,您每本書都會獲得多個條目。如果nn類別被分配給這本書,你會得到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 
+0

但是,它仍然返回所有包含至少一個給定類別的書籍,但這不是想要的行爲。 – EarthMind 2010-06-23 14:13:35

+0

這是一個好主意。 – Pointy 2010-06-23 14:14:12

+2

@Earthmind你會在最後添加一個「having」子句,並且只接受count爲2的行(或其他;你的「IN」列表中的類別數量)。 – Pointy 2010-06-23 14:14:58

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

編輯在這個問題

+0

此處您不檢查書籍分配到的分類 – chiccodoro 2010-06-23 14:18:26

+0

此查詢返回兩個類別的書籍 – pcent 2010-06-23 14:20:25

+1

right ,所以它不回答問題 – chiccodoro 2010-06-23 14:24:40

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(半連接)。

1

另一種替代方法:

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

您可以繼續鏈相交,如果你有兩個以上的類別相匹配。

+0

這將需要對每個類別單獨選擇,並且會增加代碼和操作的複雜性爲每個添加的類別。您正在索引或不必要地掃描表。 – 2010-06-23 15:30:13

+0

不確定這是否需要反對票。我添加這個例子的目的是爲了表明這實際上是一個基於集合的問題,並且存在PostgreSQL支持的「適當的」基於集合的解決方案。我從應用程序代碼的角度對性能或易用性沒有提出任何要求。事實上,我也在我自己的應用程序代碼中使用了HAVING SUM(CASE ...)方法,但是對於臨時請求,我發現INTERSECT更容易讀取和寫入。 – 2010-06-24 14:55:17

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

這是假設一本書不能同日而語的兩倍。這將選擇任一類別中的所有圖書,對類別進行計數,並確保類別數量是類別的最大數量。