PostgreSQL查询仅含指定标签值的唯一名称数据
问题说明
测试用表结构与数据如下:
with data as ( select 'John' "name", 'A' "tag", 10 "count" union all select 'John', 'B', 20 union all select 'Jane', 'A', 30 union all select 'Judith', 'A', 40 union all select 'Judith', 'B', 50 union all select 'Judith', 'C', 60 union all select 'Jason', 'D', 70 )
需求为筛选出仅关联标签A、无其他任何标签的唯一name值。
你之前的写法问题在于:仅判断了分组后去重标签数为1,没有限定标签的具体值,因此会把仅持有标签D的Jason也一并返回,不符合要求。
可用方案
通用SQL方案(兼容所有主流关系型数据库)
按name分组后同时校验两个条件即可:
- 该分组下存在标签为A的记录
- 该分组下不存在任何标签不为A的记录
select "name" from data group by "name" having sum(case when tag = 'A' then 1 else 0 end) > 0 and sum(case when tag <> 'A' then 1 else 0 end) = 0;
如果想基于你原有逻辑修正,也可以在单标签判断的基础上,补充唯一标签值的校验,写法更简洁:
select "name" from data group by "name" having count(distinct tag) = 1 and max(tag) = 'A';
注意:如果tag字段可能存在null值,建议优先用第一种sum判断的写法,避免空值带来的逻辑偏差。
PostgreSQL专属简化方案
PostgreSQL提供了更便捷的聚合函数,可以大幅简化逻辑:
- 用
bool_and判断组内所有行都满足tag='A',一步到位:
select "name" from data group by "name" having bool_and(tag = 'A');
- 也可以用数组聚合,判断去重后的标签集合仅包含A:
select "name" from data group by "name" having array_agg(distinct tag) = array['A'];
执行结果
以上所有正确写法执行后,仅会返回符合要求的Jane:
- John同时持有A、B标签,被排除
- Judith同时持有A、B、C标签,被排除
- Jason仅持有D标签,被排除
内容的提问来源于stack exchange,提问作者user554319
相关产品推荐
相关产品推荐

