web-dev-qa-db-fra.com

Comment utiliser la fonction LISTAGG d'Oracle avec un filtre unique?

J'ai une table comme celle-ci:

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

Et je veux le résultat suivant:

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

Je peux utiliser la requête suivante:

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

Pour obtenir ceci (résultat très similaire):

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

Des idées pour filtrer les noms en fonction de l'unicité dans l'appel LISTAGG?

41
daveslab

Je n'ai pas d'instance 11g disponible aujourd'hui, mais vous ne pouvez pas utiliser:

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

Super simple réponse - résolu!

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

Cela ne fonctionne que si vous spécifiez le délimiteur sur ',' pas ',' c'est-à-dire Ne fonctionne que pour aucun espace après la virgule. Si vous voulez des espaces après la virgule, voici un exemple.

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

donne BBall, Football, Hockey sur glace 

ma réponse complète ici

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

En 11g, vous pouvez utiliser la fonction non documentée wm_concat comme ceci:

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

ci-dessous n'est pas documenté et n'est pas recommandé par Oracle. et ne peut pas appliquer dans la fonction, show error

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

salutations zia

0
zia

J'avais besoin de cette paix de code en tant que sous-requête avec un filtre de données avant l'agrégation basée sur la requête la plus externe, mais je n'ai pas pu le faire avec le code de réponse choisi car ce filtre doit être placé dans la sélection la plus interne (requête de troisième niveau) et les paramètres de filtre se trouvaient dans la sélection la plus extérieure (requête de premier niveau), ce qui me donnait l'erreur ORA-00904: "TB_OUTERMOST". "COL": identificateur non valide comme le SQL ANSI indique les références de table (noms de corrélation ) sont étendus à un seul niveau. 

J'avais besoin d'une solution sans niveaux de sous-requêtes et celle-ci fonctionnait parfaitement pour moi:

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