Consulta SQL para concatenar valores de columna de varias filas en Oracle

169

¿Sería posible construir SQL para concatenar valores de columna de múltiples filas?

Lo siguiente es un ejemplo:

Tabla A

PID
UNA
si
C

Tabla B

PID SEQ Desc

A 1 Have
A 2 un bonito
A 3 días.
B 1 Buen trabajo.
C 1 Sí
C 2 podemos 
C 3 do 
C 4 este trabajo!

La salida del SQL debe ser:

PID Desc
Que tengas un buen día.
B Buen trabajo.
C Sí, podemos hacer este trabajo!

Entonces, ¿básicamente la columna Desc para la tabla de salida es una concatenación de los valores SEQ de la Tabla B?

¿Alguna ayuda con el SQL?

jagamot
fuente
Ver por ejemplo: halisway.blogspot.com/2006/08/…
Andomar
Por favor mira esta solución . Te será útil.
Jineesh Uvantavida

Respuestas:

237

Existen algunas formas según la versión que tenga: consulte la documentación de Oracle sobre técnicas de agregación de cadenas . Una muy común es usar LISTAGG:

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

Luego únete a Apara elegir lo pidsque quieras.

Nota: Fuera de la caja, LISTAGGsolo funciona correctamente con VARCHAR2columnas.

Lou Franco
fuente
2
usando wm_concat () para Oracle 10g concatena el texto en el orden ascendente del número de secuencia delimitado por comas, ¿podemos hacer descender delimitado por otra cosa?
jagamot
19

También hay una XMLAGGfunción, que funciona en versiones anteriores a 11.2. Debido a WM_CONCATque no está documentado ni respaldado por Oracle , se recomienda no usarlo en el sistema de producción.

Con XMLAGGusted puede hacer lo siguiente:

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

Lo que esto hace es

  • ponga los valores de la enamecolumna (concatenados con una coma) de la employee_namestabla en un elemento xml (con la etiqueta E)
  • extraer el texto de este
  • agregar el xml (concatenarlo)
  • llame a la columna resultante "Resultado"
Peter
fuente
XMLAGG funciona en Oracle 12.2. Además, XLMAGG permite concatar cadenas muy largas que LISTAGG puede no debido a su longitud final.
Marco
13

Con la cláusula del modelo 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.

Escribí sobre esto aquí . Y si sigue el enlace al hilo OTN, encontrará algo más, incluida una comparación de rendimiento.

Rob van Wijk
fuente
8

Como la mayoría de las respuestas sugieren, LISTAGGes la opción obvia. Sin embargo, un aspecto molesto LISTAGGes que si la longitud total de la cadena concatenada excede los 4000 caracteres (límite VARCHAR2en SQL), se genera el siguiente error, que es difícil de administrar en las versiones de Oracle hasta 12.1

ORA-01489: el resultado de la concatenación de cadenas es demasiado largo

Una nueva característica agregada en 12cR2 es la ON OVERFLOWcláusula de LISTAGG. La consulta que incluye esta cláusula se vería así:

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

Lo anterior restringirá la salida a 4000 caracteres pero no arrojará el ORA-01489error.

Estas son algunas de las opciones adicionales de la ON OVERFLOWcláusula:

  • ON OVERFLOW TRUNCATE 'Contd..' : Esto se mostrará 'Contd..'al final de la cadena (el valor predeterminado es ...)
  • ON OVERFLOW TRUNCATE '' : Esto mostrará los 4000 caracteres sin ninguna cadena de terminación.
  • ON OVERFLOW TRUNCATE WITH COUNT: Esto mostrará el número total de caracteres al final después de los caracteres de terminación. Por ejemplo: - ' ...(5512)'
  • ON OVERFLOW ERROR: Si espera LISTAGGque falle con el ORA-01489error (que de todos modos es el predeterminado).
Kaushik Nayak
fuente
6

Para aquellos que deben resolver este problema usando Oracle 9i (o anterior), probablemente necesitarán usar SYS_CONNECT_BY_PATH, ya que LISTAGG no está disponible.

Para responder al OP, la siguiente consulta mostrará el PID de la Tabla A y concatenará todas las columnas DESC de la Tabla 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;

También puede haber casos en los que las claves y los valores están contenidos en una tabla. La siguiente consulta se puede usar donde no hay Tabla A, y solo existe la Tabla 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;

Todos los valores se pueden reordenar como se desee. Las descripciones concatenadas individuales se pueden reordenar en la cláusula PARTITION BY, y la lista de PID se puede reordenar en la cláusula ORDER BY final.


Alternativamente: puede haber ocasiones en las que desee concatenar todos los valores de una tabla completa en una fila.

La idea clave aquí es usar un valor artificial para el grupo de descripciones que se concatenarán.

En la siguiente consulta, se usa la cadena constante '1', pero cualquier valor funcionará:

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;

Las descripciones concatenadas individuales se pueden reordenar en la cláusula PARTITION BY.

Varias otras respuestas en esta página también han mencionado esta referencia extremadamente útil: https://oracle-base.com/articles/misc/string-aggregation-techniques

JonathanDavidArndt
fuente
3
  1. LISTAGG ofrece el mejor rendimiento si la clasificación es obligatoria (00: 00: 05.85)

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

  2. COLLECT ofrece el mejor rendimiento si no es necesario ordenar (00: 00: 02.90):

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

  3. COLLECT con pedidos es un poco más 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;

Todas las demás técnicas fueron más lentas.

Misho
fuente
1
Sería útil elaborar su respuesta.
Jon Surrell
John, no quería repetir el artículo, pero en resumen estos son los resultados: 1. LISTAGG ofrece el mejor rendimiento si la clasificación es obligatoria (00: 00: 05.85) 2. COLLECT ofrece el mejor rendimiento si la clasificación no es necesario (00: 00: 02.90): SELECT pid, TO_STRING (CAST (COLTECT (Desc) AS varchar2_ntt)) AS Vals FROM B GROUP BY pid; 3. RECOGER con el pedido es un poco más lento (00: 00: 07.08): SELECCIONAR pid, TO_STRING (CAST (RECOGER (Desc ORDENAR POR Desc) AS varchar2_ntt)) COMO Vals DE B GROUP BY pid; Todas las demás técnicas fueron más lentas.
Misho
1
Simplemente puede editar su respuesta para incluir información relevante.
Jon Surrell
Llegué demasiado tarde en la edición y es por eso que lo agregué nuevamente. Lo siento, soy nuevo aquí y estoy empezando a entenderlo.
Misho
1

Antes de ejecutar una consulta de selección, ejecute esto:

SET SERVEROUT ON SIZE 6000

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

Prueba este código:

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

En la selección donde desea su concatenación, llame a una función SQL.

Por ejemplo:

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

Luego para la función 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 sintaxis del encabezado de función puede ser incorrecta, pero el principio funciona.

usuario5473005
fuente
Esto no es válido para Oracle
a_horse_with_no_name