117 votes

Comment puis-je combiner plusieurs lignes dans une liste délimitée par des virgules dans Oracle ?

J'ai une question simple :

select * from countries

avec les résultats suivants :

country_name
------------
Albania
Andorra
Antigua
.....

Je voudrais renvoyer les résultats en une seule ligne, comme ceci :

Albania, Andorra, Antigua, ...

Bien sûr, je peux écrire une fonction PL/SQL pour faire ce travail (je l'ai déjà fait dans Oracle 10g), mais existe-t-il une solution plus agréable, de préférence non spécifique à Oracle (ou peut-être une fonction intégrée) pour cette tâche ?

Je l'utilise généralement pour éviter les rangs multiples dans une sous-requête. Ainsi, si une personne a plus d'une nationalité, je ne veux pas qu'elle figure en double dans la liste.

Ma question est basée sur la question similaire posée sur SQL server 2005 .

UPDATE : Ma fonction ressemble à ceci :

CREATE OR REPLACE FUNCTION APPEND_FIELD (sqlstr in varchar2, sep in varchar2 ) return varchar2 is
ret varchar2(4000) := '';
TYPE cur_typ IS REF CURSOR;
rec cur_typ;
field varchar2(4000);
begin
     OPEN rec FOR sqlstr;
     LOOP
         FETCH rec INTO field;
         EXIT WHEN rec%NOTFOUND;
         ret := ret || field || sep;
     END LOOP;
     if length(ret) = 0 then
          RETURN '';
     else
          RETURN substr(ret,1,length(ret)-length(sep));
     end if;
end;

125voto

JoshL Points 4143

Le site WM_CONCAT (si elle est incluse dans votre base de données, avant Oracle 11.2) ou la fonction LISTAGG (à partir d'Oracle 11.2) devrait faire l'affaire. Par exemple, ceci permet d'obtenir une liste délimitée par des virgules des noms de tables dans votre schéma :

select listagg(table_name, ', ') within group (order by table_name) 
  from user_tables;

ou

select wm_concat(table_name) 
  from user_tables;

Plus de détails/options

Lien vers la documentation

0 votes

Cette commande est plus rapide que celle que @Decci.7 a fournie +1 et j'aime les phrases simples à une ligne :D

6 votes

Notez qu'Oracle ne recommande pas d'utiliser WM_CONCAT car il n'est pas documenté et n'est pas pris en charge : WMSYS.WM_CONCAT ne doit pas être utilisé pour les applications clients, c'est une fonction interne (Doc ID 1336219.1)

9 votes

WM_CONCAT a été abandonné en 12c. Toute personne utilisant cette fonction non documentée aura une surprise lors de la mise à jour.

96voto

Daniel Emge Points 522

Voici une méthode simple sans stragg ni création de fonction.

create table countries ( country_name varchar2 (100));

insert into countries values ('Albania');

insert into countries values ('Andorra');

insert into countries values ('Antigua');

SELECT SUBSTR (SYS_CONNECT_BY_PATH (country_name , ','), 2) csv
      FROM (SELECT country_name , ROW_NUMBER () OVER (ORDER BY country_name ) rn,
                   COUNT (*) OVER () cnt
              FROM countries)
     WHERE rn = cnt
START WITH rn = 1
CONNECT BY rn = PRIOR rn + 1;

CSV                                                                             
--------------------------
Albania,Andorra,Antigua                                                         

1 row selected.

Comme d'autres l'ont mentionné, si vous êtes sur 11g R2 ou plus, vous pouvez maintenant utiliser listagg qui est beaucoup plus simple.

select listagg(country_name,', ') within group(order by country_name) csv
  from countries;

CSV                                                                             
--------------------------
Albania, Andorra, Antigua

1 row selected.

0 votes

Belle solution courte mais quelques fautes de frappe l'entachent. Cette ligne devrait se lire FROM (SELECT nom_pays , ROW_NUMBER () OVER (ORDER BY country_name ) rn,

3 votes

La suggestion de JoshL d'utiliser la fonction LISTAGG est hautement préférable pour toute personne utilisant la version 11.2 ou plus récente.

2 votes

Assurez-vous simplement que vos résultats concaténés ne dépassent pas la limite de longueur maximale VARCHAR2 de votre base de données oracle (probablement 4000 octets), sinon vous rencontrerez le problème suivant ORA-01489 result of string concatenation is too long .

21voto

Makatun Points 158

Pour Oracle, vous pouvez utiliser LISTAGG

7 votes

Dans Oracle 11.2, comme l'a souligné JoshL.

3 votes

Le lien est rompu.

0 votes

Lien fixe. Merci

20voto

Decci.7 Points 91

Vous pouvez également l'utiliser :

SELECT RTRIM (
          XMLAGG (XMLELEMENT (e, country_name || ',')).EXTRACT ('//text()'),
          ',')
          country_name
  FROM countries;

0 votes

Merci ! Cela fonctionne dans Oracle 10g.

19voto

Gaya3 Points 79

Vous pouvez essayer cette requête.

select listagg(country_name,',') within group (order by country_name) cnt 
from countries;

0 votes

Listagg a été introduit dans Oracle 11g Release 2.

1 votes

Cela ne fonctionnerait pas s'il y a trop de colonnes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X