Query SQL per concatenare i valori di colonna da più righe in Oracle


169

Sarebbe possibile costruire SQL per concatenare i valori di colonna da più righe?

Quanto segue è un esempio:

Tabella A

PID
UN
B
C

Tabella B

PID SEQ Desc

A 1 Have
A 2 un bel
3 giorni.
B 1 Bel lavoro.
C 1 Sì
C 2 possiamo 
C 3 do 
C 4 questo lavoro!

L'output dell'SQL dovrebbe essere -

PID Desc
A Buona giornata.
B Bel lavoro.
C Sì, possiamo fare questo lavoro!

Quindi in pratica la colonna Desc per la tabella out put è una concatenazione dei valori SEQ della tabella B?

Qualche aiuto con l'SQL?



Si prega di guardare questa soluzione . Ti sarà utile.
Jineesh Uvantavida,

Risposte:


237

Esistono alcuni modi a seconda della versione in uso: consultare la documentazione di Oracle sulle tecniche di aggregazione delle stringhe . Uno molto comune è usare LISTAGG:

SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description
FROM B GROUP BY pid;

Quindi unisciti a Aper scegliere quello pidsche desideri.

Nota: pronto all'uso, LISTAGGfunziona correttamente solo con le VARCHAR2colonne.


2
utilizzando wm_concat () per Oracle 10g concatena il testo in ordine crescente del numero progressivo delimitato da virgole, possiamo fare in modo che il decrescente sia delimitato da qualcos'altro?
Jagamot,

19

C'è anche una XMLAGGfunzione, che funziona su versioni precedenti alla 11.2. Poiché nonWM_CONCAT è documentato e non è supportato da Oracle , si consiglia di non utilizzarlo nel sistema di produzione.

Con XMLAGGte puoi fare quanto segue:

SELECT XMLAGG(XMLELEMENT(E,ename||',')).EXTRACT('//text()') "Result" 
FROM employee_names

Quello che fa è

  • inserisci i valori della enamecolonna (concatenati con una virgola) dalla employee_namestabella in un elemento xml (con tag E)
  • estrarre il testo di questo
  • aggregare l'xml (concatenarlo)
  • chiama la colonna risultante "Risultato"

XMLAGG funziona su Oracle 12.2. Inoltre, XLMAGG consente di concatanare stringhe molto lunghe che LISTAGG potrebbe non avere a causa della loro lunghezza finale.
Marco

13

Con la clausola del modello 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.

Ho scritto su questo qui . E se segui il link al thread OTN ne troverai altri, incluso un confronto delle prestazioni.



8

Come la maggior parte delle risposte suggerisce, LISTAGGè l'opzione ovvia. Tuttavia, un aspetto fastidioso LISTAGGè che se la lunghezza totale della stringa concatenata supera i 4000 caratteri (limite per VARCHAR2in SQL), viene generato l'errore di seguito, che è difficile da gestire nelle versioni Oracle fino alla 12.1

ORA-01489: il risultato della concatenazione di stringhe è troppo lungo

Una nuova funzionalità aggiunta in 12cR2 è la ON OVERFLOWclausola di LISTAGG. La query che include questa clausola sarebbe simile a:

SELECT pid, LISTAGG(Desc, ' ' on overflow truncate) WITHIN GROUP (ORDER BY seq) AS desc
FROM B GROUP BY pid;

Quanto sopra limiterà l'output a 4000 caratteri ma non genererà l' ORA-01489errore.

Queste sono alcune delle opzioni aggiuntive della ON OVERFLOWclausola:

  • ON OVERFLOW TRUNCATE 'Contd..' : Verrà visualizzato 'Contd..'alla fine della stringa (l'impostazione predefinita è ...)
  • ON OVERFLOW TRUNCATE '' : Questo visualizzerà i 4000 caratteri senza alcuna stringa di terminazione.
  • ON OVERFLOW TRUNCATE WITH COUNT: Questo mostrerà il numero totale di caratteri alla fine dopo i caratteri finali. Ad esempio: - ' ...(5512)'
  • ON OVERFLOW ERROR: Se si prevede LISTAGGche fallisca con l' ORA-01489errore (che è comunque predefinito).

6

Per coloro che devono risolvere questo problema utilizzando Oracle 9i (o precedente), sarà probabilmente necessario utilizzare SYS_CONNECT_BY_PATH, poiché LISTAGG non è disponibile.

Per rispondere all'OP, la seguente query visualizzerà il PID dalla tabella A e concatenerà tutte le colonne DESC dalla tabella 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;

Ci possono anche essere casi in cui chiavi e valori sono tutti contenuti in una tabella. La seguente query può essere utilizzata in assenza della tabella A ed esiste solo la tabella 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;

Tutti i valori possono essere riordinati come desiderato. Le descrizioni concatenate individuali possono essere riordinate nella clausola PARTITION BY e l'elenco dei PID può essere riordinato nella clausola ORDER BY finale.


In alternativa: potrebbero esserci momenti in cui si desidera concatenare tutti i valori di un'intera tabella in una riga.

L'idea chiave qui sta usando un valore artificiale per il gruppo di descrizioni da concatenare.

Nella query seguente, viene utilizzata la stringa costante '1', ma qualsiasi valore funzionerà:

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;

Le descrizioni concatenate individuali possono essere riordinate nella clausola PARTITION BY.

Diverse altre risposte in questa pagina hanno anche menzionato questo riferimento estremamente utile: https://oracle-base.com/articles/misc/string-aggregation-techniques


3
  1. LISTAGG offre le migliori prestazioni se l'ordinamento è un must (00: 00: 05.85)

    SELECT pid, LISTAGG(Desc, ' ') WITHIN GROUP (ORDER BY seq) AS description FROM B GROUP BY pid;

  2. COLLECT offre le migliori prestazioni se l'ordinamento non è necessario (00: 00: 02.90):

    SELECT pid, TO_STRING(CAST(COLLECT(Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;

  3. COLLECT con l'ordinamento è un po 'più lento (00: 00: 07.08):

    SELECT pid, TO_STRING(CAST(COLLECT(Desc ORDER BY Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid;

Tutte le altre tecniche erano più lente.


1
Sarebbe utile elaborare la tua risposta.
Jon Surrell,

John, non volevo ripetere l'articolo, ma in breve questi sono i risultati: 1. LISTAGG offre le migliori prestazioni se l'ordinamento è un must (00: 00: 05.85) 2. COLLECT offre le migliori prestazioni se l'ordinamento non lo è necessario (00: 00: 02.90): SELECT pid, TO_STRING (CAST (COLLECT (Desc) AS varchar2_ntt)) AS Vals from B GROUP BY pid; 3. COLLECT con l'ordinamento è un po 'più lento (00: 00: 07.08): SELECT pid, TO_STRING (CAST (COLLECT (Desc ORDER BY Desc) AS varchar2_ntt)) AS Vals DA B GROUP BY pid; Tutte le altre tecniche erano più lente.
Misho,

1
Puoi semplicemente modificare la tua risposta per includere informazioni pertinenti.
Jon Surrell,

Ero troppo tardi nella modifica ed è per questo che l'ho aggiunto di nuovo. Mi dispiace, sono nuovo qui e ho appena iniziato a capire.
Misho,

1

Prima di eseguire una query di selezione, eseguire questo:

SET SERVEROUT ON SIZE 6000

SELECT XMLAGG(XMLELEMENT(E,SUPLR_SUPLR_ID||',')).EXTRACT('//text()') "SUPPLIER" 
FROM SUPPLIERS;

-1

Prova questo codice:

 SELECT XMLAGG(XMLELEMENT(E,fieldname||',')).EXTRACT('//text()') "FieldNames"
    FROM FIELD_MASTER
    WHERE FIELD_ID > 10 AND FIELD_AREA != 'NEBRASKA';

-3

Nel selezionare dove si desidera la concatenazione, chiamare una funzione SQL.

Per esempio:

select PID, dbo.MyConcat(PID)
   from TableA;

Quindi per la funzione 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 sintassi dell'intestazione della funzione potrebbe essere errata, ma il principio funziona.


Questo non è valido per Oracle
a_horse_with_no_name l'
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.