问题实际上并不在于两层聚合,如果那是您正在寻找三个或更多人的所有组合 - 所以listagg() 甚至对此也不起作用。
我怀疑有一种更简洁的方法,也许是使用示范条款;但是您可以使用a recursive CTE 创建每个团队至少三名成员的所有可能组合的列表:
with r (team, persons, members, last_person) as (
select team, person, 1, person
from ztp
union all
select r.team, r.persons ||','|| z.person, r.members + 1, z.person
from r
join ztp z on z.team = r.team and z.person > r.last_person
)
select * from r
where members >= 3;
anchor 子句只获取原始数据,加上一个members 列来计算有多少“聚合”值,以便我们以后可以使用它来过滤,以及最后看到的人,所以我们可以在 ' 之外使用它聚合'字符串。递归成员在同一团队中寻找更多人,将他们附加到列表中并增加成员计数,以便稍后再次过滤。
使用您的样本数据进行查询,确定五个团队中三个或更多人的 12 种不同组合:
TEAM PERSONS MEMBERS LAST_PERSON
---- ------------------------------ ---------- -----------
T1 P1,P3,P4 3 P4
T1 P1,P2,P4 3 P4
T1 P1,P2,P3 3 P3
T1 P2,P3,P4 3 P4
T2 P2,P3,P4 3 P4
T3 P2,P4,P5 3 P5
T3 P2,P3,P5 3 P5
T3 P2,P3,P4 3 P4
T3 P3,P4,P5 3 P5
T5 P3,P4,P5 3 P5
T1 P1,P2,P3,P4 4 P4
T3 P2,P3,P4,P5 4 P5
然后您可以使用listagg() 来对抗,使用having 子句过滤至少两个团队:
with r (team, persons, members, last_person) as (
select team, person, 1, person
from ztp
union all
select r.team, r.persons ||','|| z.person, r.members + 1, z.person
from r
join ztp z on z.team = r.team and z.person > r.last_person
)
select listagg(team, ',') within group (order by team) as teams, persons
from r
where members >= 3
group by persons
having count(persons) >= 2;
使用您的示例数据(此处作为另一个 CTE):
with ztp (team, person) as (
select 'T1', 'P1' from dual
union all select 'T1', 'P2' from dual
union all select 'T1', 'P3' from dual
union all select 'T1', 'P4' from dual
union all select 'T2', 'P2' from dual
union all select 'T2', 'P3' from dual
union all select 'T2', 'P4' from dual
union all select 'T3', 'P2' from dual
union all select 'T3', 'P3' from dual
union all select 'T3', 'P4' from dual
union all select 'T3', 'P5' from dual
union all select 'T4', 'P3' from dual
union all select 'T4', 'P4' from dual
union all select 'T5', 'P3' from dual
union all select 'T5', 'P4' from dual
union all select 'T5', 'P5' from dual
),
r (team, persons, members, last_person) as (
select team, person, 1, person
from ztp
union all
select r.team, r.persons ||','|| z.person, r.members + 1, z.person
from r
join ztp z on z.team = r.team and z.person > r.last_person
)
select listagg(team, ',') within group (order by team) as teams, persons
from r
where members >= 3
group by persons
having count(persons) >= 2;
TEAMS PERSONS
------------------------------ ------------------------------
T1,T2,T3 P2,P3,P4
T3,T5 P3,P4,P5