Oracle SQL在具有一个条件的两列上聚合

时间:2018-06-21 06:24:30

标签: sql oracle

具有名为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

但是结果只能聚合人员,而且我不知道如何在一个条件下在两列上聚合。

2 个答案:

答案 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