2017-06-13 44 views
0

我有一個MySQL數據庫,它看起來像:SQL過濾器和規則跨越多個行

item table 
| id | name | 

item_category link table 
| item_id | category_id | 

category table 
| id | name | 

如果我要取的是涉及到許多類別的一個項目,我可以簡單地做:

SELECT item.* 
FROM item 
JOIN item_category ON item_category.item_id = item.id 
LEFT JOIN category ON category.id = item_category.category_id 
WHERE category.name in ("category_one", "category_two") 

但是,如果我想要獲得與所有類別列表相關的項目,則問題會變得稍微複雜一些,因爲從我的查詢返回的行每個都包含一個類別。如何編寫一個查詢,其中只包含與所有類別相關的項目?

我試着寫一個查詢與嵌套的選擇是這樣的:

SELECT item.* 
FROM item 
WHERE EXISTS (
    SELECT item.id 
    FROM item_category ON item_category.item_id = item.id 
    LEFT JOIN category ON category.id = item_category.category_id 
    WHERE item_category.id = item.id 
    AND category.name = "category_one" 
) 
AND EXISTS (
    SELECT item.id 
    FROM item_category ON item_category.item_id = item.id 
    LEFT JOIN category ON category.id = item_category.category_id 
    WHERE item_category.id = item.id 
    AND category.name = "category_two" 
) 

但是,這是即使在相關領域的指標令人難以置信unperformant。

謝謝你對這個問題的任何意見。

+0

請參閱:[爲什麼我應該爲我認爲是非常簡單的SQL查詢提供一個MCVE?](https://meta.stackoverflow.com/questions/333952/why-should-i-provide-an -mcve換什麼,似乎對我將要-A-極簡單的SQL查詢)。此外,關於查詢性能的問題總是需要SHOW CREATE TABLE語句來顯示所有相關的表格以及EXPLAIN的結果。 – Strawberry

+0

順便說一句,'LEFT JOIN x ... WHERE x = ...'和'INNER JOIN x ...'一樣 – Strawberry

回答

1

執行此操作的典型方法是(1)爲每個必須匹配的值加入「類別」電纜一次,或者(2)彙總您的第一個查詢(按項目分組)和篩選,其中count(distinct category.name) =您的價值清單中的項目數量。

+0

雖然'ALL'是個特例,不是嗎? – Strawberry

+0

如果他是指存在的所有類別,是的;但我認爲它是指列表中的所有類別。 – Uueerdo

+0

我看,是的 - 也許我誤解了 – Strawberry

1

考慮以下...

DROP TABLE IF EXISTS category; 

CREATE TABLE category 
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY 
,name VARCHAR(12) NOT NULL UNIQUE 
); 

INSERT INTO category VALUES 
(101,'animals'), 
(102,'minerals'), 
(103,'vegetables'); 

DROP TABLE IF EXISTS item_category; 

CREATE TABLE item_category 
(item_id INT NOT NULL 
,category_id INT NOT NULL 
,PRIMARY KEY(item_id,category_id) 
); 

INSERT INTO item_category VALUES 
(1,101), 
(1,102), 
(1,103), 
(2,102), 
(3,101), 
(3,103); 

通過觀察,我們可以看到,只有1項與所有類別有關。

那麼,我們如何選擇那些不是?

SELECT DISTINCT ic.item_id 
      FROM item_category ic 
      JOIN category c ON c.id <> ic.category_id 
      LEFT 
      JOIN item_category x 
      ON x.item_id = ic.item_id 
      AND x.category_id = c.id 
      WHERE x.item_id IS NULL; 
+---------+ 
| item_id | 
+---------+ 
|  2 | 
|  3 | 
+---------+ 

與所有類別有關的項目列表與此組相反。