Equivalente ao GROUP_CONCAT do Postgresql?


248

Eu tenho uma tabela e gostaria de puxar uma linha por id com valores de campo concatenados.

Na minha mesa, por exemplo, tenho o seguinte:

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

E eu gostaria de mostrar:

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

No MySQL, consegui usar a função agregada GROUP_CONCAT, mas isso não parece funcionar aqui ... Existe um equivalente para o PostgreSQL, ou outra maneira de conseguir isso?





1
Eu acho que a melhor resposta ainda está em outra pergunta: stackoverflow.com/a/47638417/243233 #
11777 Jus12

Respostas:


237

Este é provavelmente um bom ponto de partida (apenas na versão 8.4+):

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

array_agg retorna um array, mas você pode convertê- lo em texto e editar conforme necessário (veja os esclarecimentos abaixo).

Antes da versão 8.4, você mesmo deveria defini-lo antes de usar:

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

(parafraseado na documentação do PostgreSQL)

Esclarecimentos:

  • O resultado de converter uma matriz em texto é que a sequência resultante inicia e termina com chaves. Esses aparelhos precisam ser removidos por algum método, se não forem desejados.
  • A conversão de ANYARRAY para TEXT simula melhor a saída CSV, pois os elementos que contêm vírgulas incorporadas são citados duas vezes na saída no estilo CSV padrão. Nem array_to_string () nem string_agg () (a função "group_concat" adicionada na 9.1) citam seqüências de caracteres com vírgulas incorporadas, resultando em um número incorreto de elementos na lista resultante.
  • A nova função 9.1 string_agg () NÃO converte os resultados internos em TEXT primeiro. Portanto, "string_agg (value_field)" geraria um erro se value_field for um número inteiro. "string_agg (value_field :: text)" seria necessário. O método array_agg () requer apenas uma conversão após a agregação (em vez de uma conversão por valor).

1
E no 9.0 você terá listagg ()
Scott Bailey

6
Para obter o CSV, a consulta deve 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
Você não pode usar array_to_string em todos os casos aqui. Se o seu value_field contiver uma vírgula incorporada, o CSV resultante estará incorreto. Usar array_agg () e converter para TEXT cita corretamente as seqüências de caracteres com vírgulas incorporadas. A única ressalva é que ele também inclui as chaves de início e fim, daí a minha declaração "e edite conforme necessário". Vou editar para esclarecer esse ponto.
Matthew Wood

FYI: aqui está um link para docs em array_agg em 8,4
Michael Rusch

256

Desde o 9.0, isso é ainda mais fácil:

SELECT id, 
       string_agg(some_column, ',')
FROM the_table
GROUP BY id

32
Note que a sintaxe também permite que você especifique a ordem dos valores na string (ou matriz, usando array_agg) por exemplo, string_agg(some_column, ',' ORDER BY some_column)ou mesmostring_agg(surname || ', ' || forename, '; ' ORDER BY surname, forename)
IMSOP

8
É impressionante que distinctfunciona com string_agg, então pode-se usarstring_agg(distinct some_solumn, ',')
Arun

3
Observe que você pode precisar converter o valor da coluna para TEXTse esse for um valor não-stringável (por exemplo, uuid). Isso seria parecido comstring_agg(some_column::text, ',')
Kendall

48
SELECT array_to_string(array(SELECT a FROM b),', ');

Vai fazer também.


É possível fazer algo como neste comentário , onde você agrega em uma determinada ordem? Como você lidaria com o agrupamento por uma coluna e a ordenação por outra (por exemplo, para concatenar variáveis ​​em um conjunto de dados longitudinal)?
Michael A

15

Tente assim:

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

2

e a versão para trabalhar no tipo de matriz :

select
  array_to_string(
    array(select distinct unnest(zip_codes) from table),
    ', '
);

Resposta duplicada, @max_spy disse a mesma coisa há cinco anos
Emil Vikström

@ EmilVikström: você tem razão em estar errado, mas leia com atenção. Não é apenas diferente, mas dei um exemplo, que funciona com o tipo de matriz - como zip_codes character varying(5)[]. Além disso, verifiquei que, para o meu propósito - é necessário desnaturar, caso contrário, você verá ERROR: cannot accumulate arrays of different dimensionality.
Sławomir Lenart

1

Minha sugestão no 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   

1
Por que você está fazendo ORDER BYuma consulta interna? A encomenda não se perde, afinal?
31818 mypetlion

-1

Espero que a consulta abaixo do Oracle 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

Eu testei em rextester.com/l/postgresql_online_compiler e não funcionou: 42883: a função listagg (texto, desconhecido, texto) não existe
Manuel Romeiro

O Oracle possui sintaxe e funções diferentes das do postgres.
Herman J. Radtke III
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.