Postgresql GROUP_CONCAT equivalente?


248

Ho una tabella e vorrei tirare una riga per ID con i valori di campo concatenati.

Nella mia tabella, ad esempio, ho questo:

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

E vorrei produrre:

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

In MySQL sono stato in grado di utilizzare la funzione aggregata GROUP_CONCAT, ma qui non sembra funzionare ... Esiste un equivalente per PostgreSQL o un altro modo per ottenere questo risultato?




possibile duplicato della funzione Simulazione group_concat di MySQL in SQL Server?
ntalbs,

1
Penso che la risposta migliore è ancora in un'altra domanda: stackoverflow.com/a/47638417/243233
Jus12

Risposte:


237

Questo è probabilmente un buon punto di partenza (solo versione 8.4+):

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

array_agg restituisce un array, ma puoi lanciarlo in testo e modificarlo secondo necessità (vedi chiarimenti, sotto).

Prima della versione 8.4, è necessario definirlo da soli prima dell'uso:

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

(parafrasato dalla documentazione PostgreSQL)

chiarimenti:

  • Il risultato del cast di una matrice nel testo è che la stringa risultante inizia e termina con parentesi graffe. Tali parentesi graffe devono essere rimosse con un metodo, se non lo si desidera.
  • La trasmissione di ANYARRAY in TEXT simula al meglio l'output CSV poiché gli elementi che contengono virgole incorporate sono racchiusi tra virgolette nell'output in stile CSV standard. Né array_to_string () o string_agg () (la funzione "group_concat" aggiunta in 9.1) citano le stringhe con virgole incorporate, risultando in un numero errato di elementi nell'elenco risultante.
  • La nuova funzione 9.1 string_agg () NON esegue prima il cast dei risultati interni su TEXT. Quindi "string_agg (value_field)" genererebbe un errore se value_field è un numero intero. "string_agg (value_field :: text)" sarebbe richiesto. Il metodo array_agg () richiede solo un cast dopo l'aggregazione (anziché un cast per valore).

1
E in 9.0 avrai listagg ()
Scott Bailey il

6
Per ottenere CSV la query dovrebbe essere: SELECT id_field, array_to_string (array_agg (value_field1), ','), array_to_string (array_agg (value_field2), ',') DA data_table GROUP BY id_field
Nux

2
Non puoi usare array_to_string in tutti i casi qui. Se il tuo value_field contiene una virgola incorporata, il CSV risultante non è corretto. L'uso di array_agg () e il casting in TEXT citano correttamente le stringhe con virgole incorporate. L'unica avvertenza è che include anche le parentesi graffe iniziali e finali, quindi la mia affermazione "e modifica secondo necessità". Modificherò per chiarire questo punto.
Matthew Wood,

Cordiali saluti: ecco un link ai documenti su array_agg in 8.4
Michael Rusch,

256

Da 9.0 questo è ancora più semplice:

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

32
Si noti che la sintassi consente anche di specificare l'ordine dei valori nella stringa (o nell'array, usando array_agg) eg string_agg(some_column, ',' ORDER BY some_column)o evenstring_agg(surname || ', ' || forename, '; ' ORDER BY surname, forename)
IMSoP

8
È fantastico che distinctfunziona con string_agg, quindi si può usarestring_agg(distinct some_solumn, ',')
arun

3
Si noti che potrebbe essere necessario eseguire il cast del valore della colonna TEXTse è un valore non stringibile (ad es. uuid). Questo sarebbe similestring_agg(some_column::text, ',')
Kendall,

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

Lo farà anche.


È possibile fare qualcosa di simile in questo commento , dove si aggrega in un certo ordine? Come gestiresti il ​​raggruppamento per colonna e l'ordinamento per un'altra (ad esempio, per concatenare le variabili all'interno di un set di dati longitudinale)?
Michael A

15

Prova in questo modo:

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

2

e la versione per funzionare sul tipo di array :

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

Risposta duplicata, @max_spy ha detto la stessa cosa cinque anni fa
Emil Vikström,

@ EmilVikström: hai ragione di sbagliarti, ma leggi attentamente. Non è solo diverso, ma ho dato un esempio, che funziona con il tipo di array - come essere zip_codes character varying(5)[]. Inoltre, ho verificato che per il mio scopo - è necessario il più inutile, altrimenti vedrai ERROR: cannot accumulate arrays of different dimensionality.
Sławomir Lenart,

1

La mia sugestion in 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
Perché stai facendo ORDER BYuna query interna? L'ordinamento non andrà comunque perso?
mypetlion,

-1

Spero che sotto la query Oracle funzionerà.

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

L'ho provato su rextester.com/l/postgresql_online_compiler e non ha funzionato: 42883: la funzione listagg (testo, sconosciuto, testo) non esiste
Manuel Romeiro

Oracle ha sintassi e funzioni diverse rispetto a Postgres.
Herman J. Radtke III,
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.