Postgres 专属标签搜索
Postgres exclusive tag search
我正在尝试 return 与一个用户相关联的所有行,该用户与所有查询 'tags' 相关联。我的 table 结构和所需的输出如下:
admin.tags:
user_id | tag | detail | date
2 | apple | blah... | 2015/07/14
3 | apple | blah. | 2015/07/17
1 | grape | blah.. | 2015/07/23
2 | pear | blahblah | 2015/07/23
2 | apple | blah, blah | 2015/07/25
2 | grape | blahhhhh | 2015/07/28
system.users:
id | email
1 | joe@test.com
2 | jane@test.com
3 | bob@test.com
queried tags:
'apple', 'pear'
desired output:
user_id | tag | detail | date | email
2 | apple | blah... | 2015/07/14 | jane@test.com
2 | pear | blahblah | 2015/07/23 | jane@test.com
2 | apple | blah, blah | 2015/07/25 | jane@test.com
由于 user_id 2 与 'apple' 和 'pear' 相关联,她的每个 'apple' 和 'pear' 行都是 returned,加入 system.users
也是为了 return 她的电子邮件。
我对如何正确设置此 postgresql 查询感到困惑。我用左反连接做了几次尝试,但似乎无法得到想要的结果。
使用相关子select统计用户不同标签的数量,使用非相关子select统计不同标签的数量:
select at.user_id, at.tag, at.detail, at.date, su.email
from admin.tags at
join system.users su on at.user_id = su.id
where (select count(distinct tag) from admin.tags at2
where at2.user_id = at.user_id)
= (select count(distinct tag) from admin.tag)
派生 table 中的查询为您提供具有所有指定标签的用户的用户 ID,外部查询为您提供详细信息。
select *
from "system.users" s
join "admin.tags" a on s.id = a.user_id
join (
select user_id
from "admin.tags"
where tag in ('apple', 'pear')
group by user_id
having count(distinct tag) = 2
) t on s.id = t.user_id;
请注意,此查询将包括具有您搜索的两个标签但也可能具有其他标签的用户,只要他们至少具有指定的两个标签即可。
使用您的样本数据,输出将是:
| id | email | user_id | tag | detail | date | user_id |
|----|---------------|---------|-------|------------|------------------------|---------|
| 2 | jane@test.com | 2 | grape | blahhhhh | July, 28 2015 00:00:00 | 2 |
| 2 | jane@test.com | 2 | apple | blah, blah | July, 25 2015 00:00:00 | 2 |
| 2 | jane@test.com | 2 | pear | blahblah | July, 23 2015 00:00:00 | 2 |
| 2 | jane@test.com | 2 | apple | blah... | July, 14 2015 00:00:00 | 2 |
如果你想排除带有 grape
的行,只需在外部查询中添加一个 where tag in ('apple', 'pear')
。
如果您只想要只搜索过标签的用户和 none 其他(例如精确划分),您可以将派生 table 中的查询更改为:
select user_id
from "admin.tags"
group by user_id
having sum(case when tag = 'apple' then 1 else 0 end) >= 1
and sum(case when tag = 'pear' then 1 else 0 end) >= 1
and sum(case when tag not in ('apple','pear') then 1 else 0 end) = 0
鉴于您的示例数据,这不会 return 任何东西,因为用户 2 也有 葡萄
must-have-them-all 类关系除法问题的标准双重否定方法:(我将 date
重命名为 zdate
以避免使用关键字作为标识符)
-- For convenience: put search arguments into a temp table or CTE
-- I cheat by extracting this from the admin_tags table
-- (in fact, there should be a table with all possible tags somwhere)
-- WITH needed_tags AS (
-- SELECT DISTINCT tag
-- FROM admin_tags
-- WHERE tag IN ('apple' , 'pear' )
-- )
-- Even better: directly use a VALUES() as a constructor
-- (thanks to @jpw )
WITH needed_tags(tag) AS (
VALUES ('apple' ) , ( 'pear' )
)
SELECT at.user_id , at.tag , at.detail , at.zdate
, su.email
FROM admin_tags at
JOIN system_users su ON su.id = at.user_id
WHERE NOT EXISTS (
SELECT * FROM needed_tags nt
WHERE NOT EXISTS (
SELECT * FROM admin_tags nx
WHERE nx.user_id = at.user_id
AND nx.tag = nt.tag
)
)
;
我正在尝试 return 与一个用户相关联的所有行,该用户与所有查询 'tags' 相关联。我的 table 结构和所需的输出如下:
admin.tags:
user_id | tag | detail | date
2 | apple | blah... | 2015/07/14
3 | apple | blah. | 2015/07/17
1 | grape | blah.. | 2015/07/23
2 | pear | blahblah | 2015/07/23
2 | apple | blah, blah | 2015/07/25
2 | grape | blahhhhh | 2015/07/28
system.users:
id | email
1 | joe@test.com
2 | jane@test.com
3 | bob@test.com
queried tags:
'apple', 'pear'
desired output:
user_id | tag | detail | date | email
2 | apple | blah... | 2015/07/14 | jane@test.com
2 | pear | blahblah | 2015/07/23 | jane@test.com
2 | apple | blah, blah | 2015/07/25 | jane@test.com
由于 user_id 2 与 'apple' 和 'pear' 相关联,她的每个 'apple' 和 'pear' 行都是 returned,加入 system.users
也是为了 return 她的电子邮件。
我对如何正确设置此 postgresql 查询感到困惑。我用左反连接做了几次尝试,但似乎无法得到想要的结果。
使用相关子select统计用户不同标签的数量,使用非相关子select统计不同标签的数量:
select at.user_id, at.tag, at.detail, at.date, su.email
from admin.tags at
join system.users su on at.user_id = su.id
where (select count(distinct tag) from admin.tags at2
where at2.user_id = at.user_id)
= (select count(distinct tag) from admin.tag)
派生 table 中的查询为您提供具有所有指定标签的用户的用户 ID,外部查询为您提供详细信息。
select *
from "system.users" s
join "admin.tags" a on s.id = a.user_id
join (
select user_id
from "admin.tags"
where tag in ('apple', 'pear')
group by user_id
having count(distinct tag) = 2
) t on s.id = t.user_id;
请注意,此查询将包括具有您搜索的两个标签但也可能具有其他标签的用户,只要他们至少具有指定的两个标签即可。
使用您的样本数据,输出将是:
| id | email | user_id | tag | detail | date | user_id |
|----|---------------|---------|-------|------------|------------------------|---------|
| 2 | jane@test.com | 2 | grape | blahhhhh | July, 28 2015 00:00:00 | 2 |
| 2 | jane@test.com | 2 | apple | blah, blah | July, 25 2015 00:00:00 | 2 |
| 2 | jane@test.com | 2 | pear | blahblah | July, 23 2015 00:00:00 | 2 |
| 2 | jane@test.com | 2 | apple | blah... | July, 14 2015 00:00:00 | 2 |
如果你想排除带有 grape
的行,只需在外部查询中添加一个 where tag in ('apple', 'pear')
。
如果您只想要只搜索过标签的用户和 none 其他(例如精确划分),您可以将派生 table 中的查询更改为:
select user_id
from "admin.tags"
group by user_id
having sum(case when tag = 'apple' then 1 else 0 end) >= 1
and sum(case when tag = 'pear' then 1 else 0 end) >= 1
and sum(case when tag not in ('apple','pear') then 1 else 0 end) = 0
鉴于您的示例数据,这不会 return 任何东西,因为用户 2 也有 葡萄
must-have-them-all 类关系除法问题的标准双重否定方法:(我将 date
重命名为 zdate
以避免使用关键字作为标识符)
-- For convenience: put search arguments into a temp table or CTE
-- I cheat by extracting this from the admin_tags table
-- (in fact, there should be a table with all possible tags somwhere)
-- WITH needed_tags AS (
-- SELECT DISTINCT tag
-- FROM admin_tags
-- WHERE tag IN ('apple' , 'pear' )
-- )
-- Even better: directly use a VALUES() as a constructor
-- (thanks to @jpw )
WITH needed_tags(tag) AS (
VALUES ('apple' ) , ( 'pear' )
)
SELECT at.user_id , at.tag , at.detail , at.zdate
, su.email
FROM admin_tags at
JOIN system_users su ON su.id = at.user_id
WHERE NOT EXISTS (
SELECT * FROM needed_tags nt
WHERE NOT EXISTS (
SELECT * FROM admin_tags nx
WHERE nx.user_id = at.user_id
AND nx.tag = nt.tag
)
)
;