Serait-il possible de construire SQL pour concaténer les valeurs de colonne de Plusieurs lignes?
Ce qui suit est un exemple:
Tableau a
PID A B C
Tableau b
PID SEQ Desc A 1 Avoir A 2 a Nice A 3 jours . B 1 Bel ouvrage . C 1 Oui C 2 nous pouvons C 3 do C 4 ce travail!
La sortie du SQL devrait être -
PID Desc Bonne journée . B Beau travail . C Oui, nous pouvons faire ce travail!
Donc, fondamentalement, la colonne Desc pour la table de sorties est une concaténation des valeurs SEQ de la table B?
Toute aide avec le SQL?
Selon votre version, vous disposez de plusieurs méthodes - voir la documentation Oracle sur les techniques d'agrégation de chaînes Un très commun consiste à utiliser LISTAGG
:
SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description
FROM B GROUP BY pid;
Joignez-vous ensuite à A
pour choisir la pids
souhaitée.
Remarque: Hors de la boîte, LISTAGG
ne fonctionne correctement qu'avec les colonnes VARCHAR2
.
Il existe également une fonction XMLAGG
, qui fonctionne sur les versions antérieures à 11.2. Étant donné que WM_CONCAT
est non documenté 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 effectuer les opérations suivantes:
SELECT XMLAGG(XMLELEMENT(E,ename||',')).EXTRACT('//text()') "Result"
FROM employee_names
Qu'est-ce que cela fait
ename
(concaténées avec une virgule) de la table employee_names
dans un élément xml (avec la balise E)Avec clause de modèle 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 à ce sujet ici . Et si vous suivez le lien vers le fil OTN, vous en trouverez d'autres, y compris une comparaison des performances.
La fonction analytique LISTAGG a été introduite dans Oracle 11g version 2, ce qui facilite grandement l'agrégation des chaînes . Si vous utilisez la version 2 de 11g, vous devez utiliser cette fonction pour l’agrégation de chaînes . Veuillez vous reporter ci-dessous à l’URL pour plus d’informations sur la concaténation de chaînes.
http://www.Oracle-base.com/articles/misc/StringAggregationTechniques.php
Comme le suggèrent la plupart des réponses, LISTAGG
est l'option évidente. Cependant, un aspect gênant avec LISTAGG
est que si la longueur totale de la chaîne concaténée dépasse 4000 caractères (limite pour VARCHAR2
en SQL), l'erreur ci-dessous est renvoyée, ce qui est difficile à gérer dans les versions Oracle jusqu'à 12.1
ORA-01489: le résultat de la concaténation de chaînes est trop long
Une nouvelle fonctionnalité ajoutée dans 12cR2 est la clause ON OVERFLOW
de LISTAGG
. La requête incluant 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 ne générera pas l'erreur ORA-01489
.
Voici quelques-unes des options supplémentaires de la clause ON OVERFLOW
:
ON OVERFLOW TRUNCATE 'Contd..'
: Ceci affichera 'Contd..'
à la fin de la chaîne (la valeur par défaut est ...
)ON OVERFLOW TRUNCATE ''
: Ceci affichera les 4000 caractères sans chaîne de fin.ON OVERFLOW TRUNCATE WITH COUNT
: Ceci affichera le nombre total de caractères à la fin après les caractères finaux . Exemple: - '...(5512)
'ON OVERFLOW ERROR
: Si vous vous attendez à ce que LISTAGG
échoue avec l'erreur ORA-01489
(qui est par défaut de toute façon).Pour ceux qui doivent résoudre ce problème avec Oracle 9i (ou une version antérieure), vous devrez probablement utiliser SYS_CONNECT_BY_PATH, car LISTAGG n’est pas disponible.
Pour répondre à l'OP, la requête suivante affiche le PID de la table A et concatène toutes les colonnes DESC de la table 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 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 également y avoir des cas où les clés et les valeurs sont toutes contenues dans une même table. La requête suivante peut être utilisée lorsqu'il n'y a pas de table A et que seule la table B existe:
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ées à volonté. Les descriptions concaténées individuelles peuvent être réorganisées dans la clause PARTITION BY et la liste des PID peut être réorganisée dans la clause finale ORDER BY.
Alternativement: il peut arriver que vous souhaitiez concaténer toutes les valeurs d'un tableau entier dans une ligne.
L'idée clé ici est d'utiliser une valeur artificielle pour le groupe de descriptions à concaténer.
Dans la requête suivante, la chaîne constante '1' est utilisée, 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;
Les descriptions concaténées individuelles peuvent être réorganisées 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
Avant de lancer une requête de sélection, lancez ceci:
SET SERVEROUT ON SIZE 6000
SELECT XMLAGG(XMLELEMENT(E,SUPLR_SUPLR_ID||',')).EXTRACT('//text()') "SUPPLIER"
FROM SUPPLIERS;
J'utilise le LISTAGG mais retourne cette chaîne pour la chaîne persane!
ma requête:
SELECT
listagg(DESCRIPTION,' , ') within group (order by DESCRIPTION)
FROM
B_CEREMONY
résultat:
'A7'1 , ,4F
Aidez-moi, s'il vous plaît.
wow cette solution est travaillée:
SELECT listagg(convert(DESCRIPTION, 'UTF8', 'AL16UTF16'),' , ') within group
(order by DESCRIPTION)
FROM B_CEREMONY;