it-swarm.it

Come utilizzare la funzione LISTAGG di Oracle con un filtro univoco?

Ho una tabella come questa:

group_id  name  
--------  ----
1         David
1         John
1         Alan
1         David
2         Julie
2         Charles

E voglio il seguente risultato:

group_id  names
--------  -----
1         'Alan, David, John'
2         'Charles, Julie'

Posso usare la seguente query:

select group_id, 
       listagg(name, ',') within group (order by name) as names
from demotable
group by group_id 

Per ottenere questo risultato (molto simile):

group_id  names
--------  -----
1         'Alan, David, David, John'
2         'Charles, Julie'

Qualche idea su come posso filtrare i nomi per unicità nella chiamata LISTAGG?

41
daveslab

Oggi non ho un'istanza di 11g disponibile ma non potresti usare:

SELECT group_id,
       LISTAGG(name, ',') WITHIN GROUP (ORDER BY name) AS names
  FROM (
       SELECT UNIQUE
              group_id,
              name
         FROM demotable
       )
 GROUP BY group_id
40
Ollie

Risposta super semplice - risolto!

select group_id, 
regexp_replace(
    listagg(name, ',') within group (order by name)
    ,'([^,]+)(,\1)*(,|$)', '\1\3')
from demotable
group by group_id;  

Funziona solo se si specifica il delimitatore su "," non "," cioè Funziona solo per nessuno spazio dopo la virgola. Se vuoi spazi dopo la virgola - ecco un esempio come.

select 
replace(
    regexp_replace(
     regexp_replace('BBall, BBall, BBall, Football, Ice Hockey ',',\s*',',')            
    ,'([^,]+)(,\1)*(,|$)', '\1\3')
,',',', ') 
from dual

dà BBall, Calcio, Hockey su ghiaccio 

la mia risposta completa qui

15
ozmike
create table demotable(group_id number, name varchar2(100));
insert into demotable values(1,'David');
insert into demotable values(1,'John');
insert into demotable values(1,'Alan');
insert into demotable values(1,'David');
insert into demotable values(2,'Julie');
insert into demotable values(2,'Charles');
commit;

select group_id, 
       (select listagg(column_value, ',') within group (order by column_value) from table(coll_names)) as names
from (
  select group_id, collect(distinct name) as coll_names 
    from demotable
    group by group_id 
)

GROUP_ID    NAMES
1   Alan,David,John
2   Charles,Julie
5
Michal Satny
select group_id, 
       listagg(name, ',') within group (order by name) as names
       over (partition by group_id)   
from demotable
group by group_id 
2
PJA

In 11g puoi usare la funzione non documentata wm_concat in questo modo:

     select wm_concat(distinct name) as names from demotable group by group_id
0
marko

Avevo bisogno di questa pace di codice come subquery con alcuni filtri di dati prima dell'aggregazione basata sulla maggior parte delle query esterne, ma non ero in grado di farlo utilizzando il codice di risposta scelto perché questo filtro dovrebbe andare nella selezione più interna (query di terzo livello) e i parametri del filtro erano nella selezione più esterna (query di primo livello), che mi ha dato l'errore ORA-00904: "TB_OUTERMOST". "COL": identificatore non valido poiché l'ANSI SQL indica i riferimenti di tabella (nomi di correlazione ) sono rivolti a un solo livello di profondità. 

Avevo bisogno di una soluzione senza livelli di subquery e questa sotto funzionava alla grande per me:

with

demotable as
(
  select 1 group_id, 'David'   name from dual union all
  select 1 group_id, 'John'    name from dual union all
  select 1 group_id, 'Alan'    name from dual union all
  select 1 group_id, 'David'   name from dual union all
  select 2 group_id, 'Julie'   name from dual union all
  select 2 group_id, 'Charlie' name from dual
)

select distinct 
  group_id, 
  listagg(name, ',') within group (order by name) over (partition by group_id) names
from demotable
-- where any filter I want
group by group_id, name
order by group_id;
0
Felypp Oliveira