Requête SQL aux valeurs de colonnes de concaténer plusieurs lignes dans Oracle
-
11-10-2019 - |
Question
Serait-il possible de construire SQL aux valeurs de colonnes de concaténer plusieurs lignes?
Voici un exemple:
Tableau A
PID A B C
Tableau B
PID SEQ Desc A 1 Have A 2 a nice A 3 day. B 1 Nice Work. C 1 Yes C 2 we can C 3 do C 4 this work!
Sortie de SQL doit être -
PID Desc A Have a nice day. B Nice Work. C Yes we can do this work!
Donc, fondamentalement, la colonne Desc pour sortir la table de vente est une concaténation des valeurs de SEQ du tableau B?
Toute aide avec le SQL?
La solution
Il y a quelques façons selon la version que vous avez - consultez la documentation oracle sur les techniques d'agrégation chaîne . Une très courante consiste à utiliser LISTAGG
:
SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description
FROM B GROUP BY pid;
Alors rejoignez à A
pour choisir la pids
que vous voulez.
Remarque:. Hors de la boîte, LISTAGG
ne fonctionne correctement avec des colonnes de VARCHAR2
Autres conseils
Il y a aussi une fonction XMLAGG
, qui fonctionne sur les versions antérieures à 11.2. Parce que WM_CONCAT
est non documentée et non pris en charge par Oracle , il est recommandé de ne pas l'utiliser dans le système de production.
Avec XMLAGG
vous pouvez faire ce qui suit:
SELECT XMLAGG(XMLELEMENT(E,ename||',')).EXTRACT('//text()') "Result"
FROM employee_names
Qu'est-ce que cela fait est
- mettre les valeurs de la colonne de
ename
(concaténé avec une virgule) à partir de la table deemployee_names
dans un élément XML (avec étiquette E) - extraire le texte de cette
- agréger les données XML (concaténer)
- appeler la colonne résultante "Résultat"
Avec clause type SQL:
SQL> select pid
2 , ltrim(sentence) sentence
3 from ( select pid
4 , seq
5 , sentence
6 from b
7 model
8 partition by (pid)
9 dimension by (seq)
10 measures (descr,cast(null as varchar2(100)) as sentence)
11 ( sentence[any] order by seq desc
12 = descr[cv()] || ' ' || sentence[cv()+1]
13 )
14 )
15 where seq = 1
16 /
P SENTENCE
- ---------------------------------------------------------------------------
A Have a nice day
B Nice Work.
C Yes we can do this work!
3 rows selected.
J'ai écrit au sujet de cette . Et si vous suivez le lien vers le OTN-fil, vous trouverez un peu plus, y compris une comparaison des performances.
LISTAGG fonction analytique a été introduit dans Oracle 11g Release 2 , ce qui rend très facile à cordes total. Si vous utilisez 11g Release 2, vous devez utiliser cette fonction pour l'agrégation de chaîne. S'il vous plaît se référer ci-dessous URL pour plus d'informations sur la concaténation de chaîne.
http://www.oracle-base.com/articles/misc/ StringAggregationTechniques.php
Comme la plupart des réponses suggèrent, LISTAGG
est l'option évidente. Cependant, un aspect ennuyeux avec LISTAGG
est que si la longueur totale de la chaîne concaténée est supérieure à 4000 caractères (limite pour VARCHAR2
dans SQL), l'erreur ci-dessous est levée, ce qui est difficile à gérer dans les versions Oracle jusqu'à 12.1
ORA-01489: résultat de concaténation de chaîne est trop longue
Une nouvelle fonctionnalité dans 12cR2 est la clause ON OVERFLOW
de LISTAGG
.
La requête, y compris cette clause ressemblerait à ceci:
SELECT pid, LISTAGG(Desc, ' ' on overflow truncate) WITHIN GROUP (ORDER BY seq) AS desc
FROM B GROUP BY pid;
Ce qui précède limitera la sortie à 4000 caractères mais pas jeter l'erreur ORA-01489
.
Voici quelques-unes des options supplémentaires de la clause de ON OVERFLOW
:
-
ON OVERFLOW TRUNCATE 'Contd..'
: Ceci affichera'Contd..'
à la fin de la chaîne (par défaut...
) -
ON OVERFLOW TRUNCATE ''
: Cela permet d'afficher les 4000 caractères sans aucune chaîne de terminaison. -
ON OVERFLOW TRUNCATE WITH COUNT
: Cela permet d'afficher le total nombre de caractères à la fin après que les caractères de terminaison. Par exemple: - '...(5512)
' -
ON OVERFLOW ERROR
: Si vous attendez laLISTAGG
échouer avec le erreur deORA-01489
(qui est par défaut de toute façon).
Pour ceux qui doivent résoudre ce problème en utilisant Oracle 9i (ou plus tôt), vous aurez probablement besoin d'utiliser SYS_CONNECT_BY_PATH, puisque LISTAGG n'est pas disponible.
Pour répondre à l'OP, la requête suivante affiche le PID du tableau A et concaténer toutes les colonnes du tableau B DESC:
SELECT pid, SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
SELECT ROW_NUMBER () OVER (PARTITION BY pid ORDER BY pid, seq) rnum, pid, description
FROM (
SELECT a.pid, seq, description
FROM table_a a, table_b b
WHERE a.pid = b.pid(+)
)
)
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1 AND PRIOR pid = pid
GROUP BY pid
ORDER BY pid;
Il peut aussi y avoir des cas où les clés et les valeurs sont toutes contenues dans une table. La requête suivante peut être utilisé là où il n'y a pas le tableau A, et seulement existe Tableau B:
SELECT pid, SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
SELECT ROW_NUMBER () OVER (PARTITION BY pid ORDER BY pid, seq) rnum, pid, description
FROM (
SELECT pid, seq, description
FROM table_b
)
)
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1 AND PRIOR pid = pid
GROUP BY pid
ORDER BY pid;
Toutes les valeurs peuvent être réorganisés comme vous le souhaitez. descriptions concaténés individuelles peuvent être réorganisés dans la clause PARTITION BY, et la liste des PIDs peuvent être réorganisés dans l'ordonnance définitive PAR clause.
Alternativement:. il peut y avoir des moments où vous voulez concaténer toutes les valeurs d'une table entière en une seule ligne
L'idée clé est ici en utilisant une valeur artificielle pour le groupe de descriptions à concaténer.
Dans la requête suivante, la chaîne constante « 1 » est utilisé, mais toute valeur fonctionnera:
SELECT SUBSTR (MAX (SYS_CONNECT_BY_PATH (description, ', ')), 3) all_descriptions
FROM (
SELECT ROW_NUMBER () OVER (PARTITION BY unique_id ORDER BY pid, seq) rnum, description
FROM (
SELECT '1' unique_id, b.pid, b.seq, b.description
FROM table_b b
)
)
START WITH rnum = 1
CONNECT BY PRIOR rnum = rnum - 1;
Par descriptions concaténer peuvent être réorganisés dans la clause PARTITION BY.
Plusieurs autres réponses sur cette page ont également mentionné cette référence extrêmement utile: https://oracle-base.com/articles/misc/string-aggregation-techniques
-
LISTAGG offre les meilleures performances si le tri est un must (00: 00: 05,85)
SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description FROM B GROUP BY pid;
-
COLLECT offre les meilleures performances si le tri n'est pas nécessaire (00: 00: 02,90):
SELECT pid, TO_STRING(CAST(COLLECT(Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;
-
collectez avec commande est un peu plus lent (00: 00: 07,08):
SELECT pid, TO_STRING(CAST(COLLECT(Desc ORDER BY Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;
Toutes les autres techniques ont été plus lents.
Avant d'exécuter une requête de sélection, exécutez:
SET SERVEROUT ON SIZE 6000
SELECT XMLAGG(XMLELEMENT(E,SUPLR_SUPLR_ID||',')).EXTRACT('//text()') "SUPPLIER"
FROM SUPPLIERS;
Je l'aide de la LISTAGG mais retourner cette chaîne pour la chaîne Persian!
ma requête:
SELECT
listagg(DESCRIPTION,' , ') within group (order by DESCRIPTION)
FROM
B_CEREMONY
Résultat:
'A7'1 , ,4F
S'il vous plaît aidez-moi.
wow cette solution est travaillée:
SELECT listagg(convert(DESCRIPTION, 'UTF8', 'AL16UTF16'),' , ') within group
(order by DESCRIPTION)
FROM B_CEREMONY;
Essayez ce code:
SELECT XMLAGG(XMLELEMENT(E,fieldname||',')).EXTRACT('//text()') "FieldNames"
FROM FIELD_MASTER
WHERE FIELD_ID > 10 AND FIELD_AREA != 'NEBRASKA';
Dans la sélection où vous voulez que votre concaténation, appelez une fonction SQL.
Par exemple:
select PID, dbo.MyConcat(PID)
from TableA;
Ensuite, pour la fonction SQL:
Function MyConcat(@PID varchar(10))
returns varchar(1000)
as
begin
declare @x varchar(1000);
select @x = isnull(@x +',', @x, @x +',') + Desc
from TableB
where PID = @PID;
return @x;
end
La syntaxe d'en-tête de fonction peut-être tort, mais le principe fonctionne.