具有名为ztp的Oracle 11g表,数据如下:
TEAM PERSON
---------- ----------
T1 P1
T1 P2
T1 P3
T1 P4
T2 P2
T2 P3
T2 P4
T3 P2
T3 P3
T3 P4
T3 P5
T4 P3
T4 P4
T5 P3
T5 P4
T5 P5
我希望通过sql查询汇总具有以下条件的团队和人员:至少3个相同的人加入了至少2个相同的团队。
对于表ztp中的数据,我想要这样的sql查询输出:
TEAMS PERSONS
---------- ----------
T1,T2,T3 P2,P3,P4
T3,T5 P3,P4,P5
我只这样知道listagg:
SELECT TEAM, LISTAGG(PERSON,',') WITHIN GROUP (ORDER BY PERSON) AS PERSONS
FROM ZTP
GROUP BY TEAM
但是结果只能聚合人员,而且我不知道如何在一个条件下在两列上聚合。
答案 0 :(得分:3)
问题实际上不是聚合的两层,如果那是您要寻找三个或三个以上人的所有组合,那么listagg()
甚至对此都不起作用。
我怀疑有一个更干净的方法,也许可以使用model子句;但您可以使用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
答案 1 :(得分:2)
该解决方案有点简单,可能会导致大量数据出现性能问题,但仍然可以帮助您创建更精确的解决方案。 这个想法是要创建一个成员人数超过3的人员小组,并计算该小组参加的团队数量。
假设:团队或个人名称中没有逗号。
with src as (
select 'T1' as team, 'P1' as person 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
),
cnct as (
select level lv, team, lpad(' ',level,' ')||person, sys_connect_by_path(person,',') pth
from src s1
connect by person > prior person and team=prior team
)
select tm, ltrim(pth,',')
from (
select pth, LISTAGG(team,',') within group ( order by team ) tm
from cnct
where lv >= 3
group by pth
)
where instr(tm,',') > 0