Comment combiner plusieurs lignes dans une liste délimitée par des virgules dans Oracle? [dupliquer]
-
19-08-2019 - |
Question
Cette question a déjà une réponse ici:
J'ai une requête simple:
select * from countries
avec les résultats suivants:
country_name
------------
Albania
Andorra
Antigua
.....
Je voudrais renvoyer les résultats dans une ligne, donc comme ceci:
Albania, Andorra, Antigua, ...
Bien sûr, je peux écrire une fonction PL / SQL pour faire le travail (je l’ai déjà fait dans Oracle 10g), mais existe-t-il une solution plus intéressante, de préférence non spécifique à Oracle (ou peut-être une fonction intégrée)? pour cette tâche?
Je l'utilisais généralement pour éviter la multiplication de lignes dans une sous-requête. Ainsi, si une personne a plus d'une citoyenneté, je ne veux pas qu'elle soit un doublon dans la liste.
Ma question est basée sur la question similaire sur SQL Server 2005 .
MISE À JOUR : 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;
La solution
Voici un moyen simple sans stragg ni créer 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 utilisez une version 11g R2 ou supérieure, vous pouvez maintenant utiliser listagg, ce 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.
Autres conseils
La fonction WM_CONCAT
(si incluse dans votre base de données, antérieure à Oracle 11.2) ou LISTAGG
(à partir d'Oracle 11.2) devrait très bien faire l'affaire. Par exemple, cela donne une liste séparée par des virgules des noms de table 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;
Pour Oracle, vous pouvez utiliser LISTAGG
Vous pouvez également l'utiliser:
SELECT RTRIM (
XMLAGG (XMLELEMENT (e, country_name || ',')).EXTRACT ('//text()'),
',')
country_name
FROM countries;
vous pouvez essayer cette requête.
select listagg(country_name,',') within group (order by country_name) cnt
from countries;
Le moyen le plus rapide consiste à utiliser la fonction de collecte Oracle.
Vous pouvez également faire ceci:
select *
2 from (
3 select deptno,
4 case when row_number() over (partition by deptno order by ename)=1
5 then stragg(ename) over
6 (partition by deptno
7 order by ename
8 rows between unbounded preceding
9 and unbounded following)
10 end enames
11 from emp
12 )
13 where enames is not null
Rendez-vous sur le site ask tom et cherchez "stragg" ou "string concatenation". Beaucoup de exemples. Il existe également une fonction oracle non documentée pour répondre à vos besoins.
J'avais besoin de quelque chose de similaire et j'ai trouvé la solution suivante.
select RTRIM(XMLAGG(XMLELEMENT(e,country_name || ',')).EXTRACT('//text()'),',') country_name from
Dans cet exemple, nous créons une fonction pour apporter une liste, séparée par des virgules, des motifs de retenue de la facture AP au niveau de la ligne distincts dans un champ pour la requête au niveau de l'en-tête:
FUNCTION getHoldReasonsByInvoiceId (p_InvoiceId IN NUMBER) RETURN VARCHAR2
IS
v_HoldReasons VARCHAR2 (1000);
v_Count NUMBER := 0;
CURSOR v_HoldsCusror (p2_InvoiceId IN NUMBER)
IS
SELECT DISTINCT hold_reason
FROM ap.AP_HOLDS_ALL APH
WHERE status_flag NOT IN ('R') AND invoice_id = p2_InvoiceId;
BEGIN
v_HoldReasons := ' ';
FOR rHR IN v_HoldsCusror (p_InvoiceId)
LOOP
v_Count := v_COunt + 1;
IF (v_Count = 1)
THEN
v_HoldReasons := rHR.hold_reason;
ELSE
v_HoldReasons := v_HoldReasons || ', ' || rHR.hold_reason;
END IF;
END LOOP;
RETURN v_HoldReasons;
END;
J'ai toujours dû écrire du PL / SQL pour cela ou je concaténais simplement un ',' dans le champ, copiez-le dans un éditeur et supprimez le CR de la liste en me donnant la ligne unique.
C’est-à-dire
select country_name||', ' country from countries
Un peu long dans les deux sens.
Si vous examinez Ask Tom, vous verrez des tonnes de solutions possibles, mais elles reviendront toutes aux déclarations de type et / ou PL / SQL
.SELECT REPLACE(REPLACE
((SELECT TOP (100) PERCENT country_name + ', ' AS CountryName
FROM country_name
ORDER BY country_name FOR XML PATH('')),
'&<CountryName>', ''), '&<CountryName>', '') AS CountryNames
vous pouvez utiliser cette requête pour effectuer la tâche ci-dessus
DECLARE @test NVARCHAR(max)
SELECT @test = COALESCE(@test + ',', '') + field2 FROM #test SELECT field2= @test
pour plus de détails et une explication étape par étape, visitez le lien suivant
http: //oops-solution.blogspot .com / 2011/11 / sql-server-convert-table-column-data.html