在多个查询中排除帖子中的标记

时间:2017-02-15 23:23:30

标签: postgresql

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

答案 0 :(得分: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) 

答案 1 :(得分:0)

使用子查询的一个解决方案是:

getData(): Observable<MyData> {
    // try to use getRegularData, and return observable for result.
    // if getRegularData returns null, get data from getAlternateData()
    // instead and return observable for result.
}

我不确定此查询的效率如何,但它绝对不是最具可读性的。