2017-02-15 60 views
1

裏使用Postgres我有3個表:排除從後許多標籤許多查詢

CREATE TABLE post (id SERIAL, body TEXT); 
CREATE TABLE tag (id SERIAL, name TEXT); 
CREATE TABLE post_tag (post_id INT, tag_id INT); 

INSERT INTO post(body) values('post 1'); 
INSERT INTO post(body) values('post 2'); 
INSERT INTO tag(name) values('a'); 
INSERT INTO tag(name) values('b'); 
INSERT INTO post_tag values(1, 1); 
INSERT INTO post_tag values(1, 2); 
INSERT INTO post_tag values(2, 1); 

因此post 1有標籤a, bpost 2a作爲標記。

問題:如何選擇沒有標籤b,這意味着它應該只選擇post 2所有帖子。

此查詢是不好的,因爲它會選擇這兩個職位因爲post 1有2個標籤a & b

SELECT post.* 
FROM post 
JOIN post_tag ON post_tag.post_id = post.id 
JOIN tag ON tag.id = post_tag.tag_id 
WHERE tag.name != 'b'; 

下面的作品該查詢,但是是錯誤的,因爲如果有一個標籤aaaaaaab則將匹配太:

SELECT post.id, post.body, string_agg(tag1.name, ', ') 
FROM post 
JOIN post_tag ON post_tag.post_id = post.id 
JOIN tag ON tag.id = post_tag.tag_id 
GROUP BY post.id, post.body 
HAVING string_agg(tag.name, ', ') not like '%b, %'; 

我正在尋找一個「正確」的,高效的方法來此。

編輯:該查詢還應匹配根本沒有任何標籤的帖子。

回答

2

您可以選擇使用查詢彙總標籤的帖子:

select p.id, p.body, array_agg(t.name) tags 
from post p 
left join post_tag pt on pt.post_id = p.id 
left join tag t on pt.tag_id = t.id 
group by 1, 2; 

id | body | tags 
----+--------+------- 
    1 | post 1 | {a,b} 
    2 | post 2 | {a} 
(2 rows)  

有了充足的修改,您可以使用查詢過濾數據,例如

select p.id, p.body 
from post p 
left join post_tag pt on pt.post_id = p.id 
left join tag t on pt.tag_id = t.id 
group by 1, 2 
having 'b' <> all(array_agg(t.name)); 
-- or to get also posts without tags: 
-- having 'b' <> all(array_agg(t.name)) or array_agg(t.name) = '{null}'; 

id | body 
----+-------- 
    2 | post 2 
(1 row) 
+0

謝謝!那些沒有標籤的帖子怎麼樣?邏輯上也應該是結果。對不起,我以前沒有想過這個。 –

+0

查看編輯答案。 – klin

0

一個解決方案,使用子查詢是這樣的:

SELECT * 
FROM post 
WHERE post.id IN (
    SELECT post_id 
    FROM post_tag 
    WHERE post_id != ALL (
    SELECT post_id 
    FROM post_tag 
    WHERE tag_id = (
     SELECT id 
     FROM tag 
     WHERE name = 'b' 
    ) 
    ) 
); 

我不知道該查詢但是效率如何,雖然,它絕對不是最可讀的。