Postgresql GROUP_CONCAT equivalente?

248

Tengo una tabla y me gustaría extraer una fila por id con valores de campo concatenados.

En mi tabla, por ejemplo, tengo esto:

TM67 | 4  | 32556
TM67 | 9  | 98200
TM67 | 72 | 22300
TM99 | 2  | 23009
TM99 | 3  | 11200

Y me gustaría dar salida:

TM67 | 4,9,72 | 32556,98200,22300
TM99 | 2,3    | 23009,11200

En MySQL pude usar la función agregada GROUP_CONCAT, pero eso no parece funcionar aquí ... ¿Hay un equivalente para PostgreSQL u otra forma de lograr esto?

TwixxyKit
fuente
No es una respuesta, pero echa un vistazo a postgresonline.com/journal/index.php?/archives/… .
Kuberchaun
posible duplicado de la función Simulación de MySQL group_concat en SQL Server?
ntalbs
1
Creo que la mejor respuesta todavía está en otra pregunta: stackoverflow.com/a/47638417/243233
Jus12

Respuestas:

237

Este es probablemente un buen punto de partida (solo la versión 8.4+):

SELECT id_field, array_agg(value_field1), array_agg(value_field2)
FROM data_table
GROUP BY id_field

array_agg devuelve una matriz, pero puede ENVIARLA al texto y editarla según sea necesario (consulte las aclaraciones a continuación).

Antes de la versión 8.4, debe definirlo usted mismo antes de usar:

CREATE AGGREGATE array_agg (anyelement)
(
    sfunc = array_append,
    stype = anyarray,
    initcond = '{}'
);

(parafraseado de la documentación de PostgreSQL)

Aclaraciones:

  • El resultado de convertir una matriz en texto es que la cadena resultante comienza y termina con llaves. Esos brackets deben eliminarse por algún método, si no se desean.
  • Transmitir ANYARRAY a TEXT simula mejor la salida CSV ya que los elementos que contienen comas incrustadas se citan dos veces en la salida en el estilo CSV estándar. Ni array_to_string () ni string_agg () (la función "group_concat" agregada en 9.1) citan cadenas con comas incrustadas, lo que resulta en un número incorrecto de elementos en la lista resultante.
  • La nueva función 9.1 string_agg () NO convierte primero los resultados internos a TEXTO. Entonces "string_agg (value_field)" generaría un error si value_field es un entero. Se requeriría "string_agg (value_field :: text)". El método array_agg () requiere solo una conversión después de la agregación (en lugar de una conversión por valor).
Matthew Wood
fuente
1
Y en 9.0 tendrás listagg ()
Scott Bailey
66
Para obtener CSV, la consulta debe ser: SELECT id_field, array_to_string (array_agg (value_field1), ','), array_to_string (array_agg (value_field2), ',') FROM data_table GROUP BY id_field
Nux
2
No puede usar array_to_string en todos los casos aquí. Si su value_field contiene una coma incrustada, el CSV resultante es incorrecto. El uso de array_agg () y la conversión a TEXTO cita correctamente las cadenas con comas incrustadas. La única advertencia es que también incluye los corchetes iniciales y finales, de ahí mi declaración "y editar según sea necesario". Lo editaré para aclarar ese punto.
Matthew Wood
FYI: aquí hay un enlace a documentos en array_agg en 8.4
Michael Rusch
256

Desde 9.0 esto es aún más fácil:

SELECT id, 
       string_agg(some_column, ',')
FROM the_table
GROUP BY id
un caballo sin nombre
fuente
32
Tenga en cuenta que la sintaxis también le permite especificar el orden de los valores en la cadena (o matriz, usando array_agg), por ejemplo, string_agg(some_column, ',' ORDER BY some_column)o inclusostring_agg(surname || ', ' || forename, '; ' ORDER BY surname, forename)
IMSoP
8
Es increíble que distinctfuncione con string_agg, por lo que se puede usarstring_agg(distinct some_solumn, ',')
arun el
3
Tenga en cuenta que es posible que deba convertir el valor de la columna TEXTsi es un valor no encadenable (es decir uuid). Esto se vería asístring_agg(some_column::text, ',')
Kendall, el
48
SELECT array_to_string(array(SELECT a FROM b),', ');

Lo haré igual.

genobis
fuente
¿Es posible hacer algo como en este comentario , donde se agregan en un cierto orden? ¿Cómo manejaría la agrupación por una columna y la ordenación por otra (por ejemplo, para concatenar variables dentro de un conjunto de datos longitudinales)?
Michael A
15

Intenta así:

select field1, array_to_string(array_agg(field2), ',')
from table1
group by field1;
max_spy
fuente
2

y la versión para trabajar en el tipo de matriz :

select
  array_to_string(
    array(select distinct unnest(zip_codes) from table),
    ', '
);
Sławomir Lenart
fuente
Respuesta duplicada, @max_spy dijo lo mismo hace cinco años
Emil Vikström,
@ EmilVikström: tienes derecho a equivocarte, pero lee con cuidado. No solo es diferente, sino que di un ejemplo, que funciona con el tipo de matriz, como ser zip_codes character varying(5)[]. Además, he verificado que, para mi propósito, se necesita unnest; de lo contrario, verá ERROR: cannot accumulate arrays of different dimensionality.
Sławomir Lenart
1

Mi sugestion en postgresql

SELECT cpf || ';' || nome || ';' || telefone  
FROM (
      SELECT cpf
            ,nome
            ,STRING_AGG(CONCAT_WS( ';' , DDD_1, TELEFONE_1),';') AS telefone 
      FROM (
            SELECT DISTINCT * 
            FROM temp_bd 
            ORDER BY cpf DESC ) AS y
      GROUP BY 1,2 ) AS x   
Lucas Cabral
fuente
1
¿Por qué estás haciendo ORDER BYuna consulta interna? ¿No se perderá el pedido de todos modos?
mypetlion
-1

Espero que debajo de Oracle la consulta funcione.

Select First_column,LISTAGG(second_column,',') 
    WITHIN GROUP (ORDER BY second_column) as Sec_column, 
    LISTAGG(third_column,',') 
    WITHIN GROUP (ORDER BY second_column) as thrd_column 
FROM tablename 
GROUP BY first_column
kiruba
fuente
Lo probé en rextester.com/l/postgresql_online_compiler y no funcionó: 42883: la función listagg (texto, desconocido, texto) no existe
Manuel Romeiro
Oracle tiene diferentes sintaxis y funciones que postgres.
Herman J. Radtke III