2017-02-15 21 views
1

に多くのポストからタグを除外:は、私は3つのテーブル持っているPostgresの使用多くのクエリ

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 1a, bpost 2がタグとしてaを持つタグを持っています。

質問:タグ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' 
    ) 
    ) 
); 

私はこのクエリはしかしあるしかしどのように効率的で、それはほとんど間違いなく最も読みやすいではありませんか分かりません。