Setara dengan Postgresql GROUP_CONCAT?

248

Saya punya tabel dan saya ingin menarik satu baris per id dengan nilai bidang yang digabungkan.

Di meja saya, misalnya, saya punya ini:

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

Dan saya ingin menampilkan:

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

Di MySQL saya bisa menggunakan fungsi agregat GROUP_CONCAT, tetapi sepertinya tidak berfungsi di sini ... Apakah ada yang setara untuk PostgreSQL, atau cara lain untuk mencapai ini?

TwixxyKit
sumber
Bukan jawaban, tetapi periksa postgresonline.com/journal/index.php?/archives/… .
Kuberchaun
1
Saya pikir jawaban terbaik masih dalam pertanyaan lain: stackoverflow.com/a/47638417/243233
Jus12

Jawaban:

237

Ini mungkin merupakan titik awal yang baik (hanya versi 8.4+):

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

array_agg mengembalikan sebuah array, tetapi Anda dapat melakukan CAST untuk mengirim dan mengedit sesuai kebutuhan (lihat klarifikasi, di bawah).

Sebelum versi 8.4, Anda harus menentukan sendiri sebelum menggunakannya:

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

(diparafrasekan dari dokumentasi PostgreSQL)

Klarifikasi:

  • Hasil dari casting array ke teks adalah string yang dihasilkan dimulai dan diakhiri dengan kurung kurawal. Kawat gigi itu perlu dihilangkan dengan beberapa metode, jika tidak diinginkan.
  • Casting ANYARRAY ke TEXT paling baik mensimulasikan output CSV karena elemen yang mengandung koma yang melekat dikutip dalam output dalam gaya CSV standar. Baik array_to_string () atau string_agg () (fungsi "group_concat" ditambahkan pada 9.1) mengutip string dengan koma yang disematkan, menghasilkan jumlah elemen yang salah dalam daftar yang dihasilkan.
  • Fungsi 9,1 string_agg () yang baru TIDAK mengirimkan hasil dalam ke TEXT terlebih dahulu. Jadi "string_agg (value_field)" akan menghasilkan kesalahan jika value_field adalah bilangan bulat. "string_agg (value_field :: text)" akan diperlukan. Metode array_agg () hanya membutuhkan satu gips setelah agregasi (bukan gips per nilai).
Matthew Wood
sumber
1
Dan pada 9.0 Anda akan memiliki listagg ()
Scott Bailey
6
Untuk mendapatkan CSV kueri harus: 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
Anda tidak dapat menggunakan array_to_string dalam semua kasus di sini. Jika value_field Anda berisi koma yang disematkan, CSV yang dihasilkan salah. Menggunakan array_agg () dan casting ke TEXT dengan benar mengutip string dengan koma yang disematkan. Satu-satunya peringatan adalah bahwa itu juga termasuk kurung kurawal dan akhir, maka pernyataan saya "dan edit sesuai kebutuhan". Saya akan mengedit untuk memperjelas hal itu.
Matthew Wood
FYI: inilah tautan ke dokumen pada array_agg di 8,4
Michael Rusch
256

Sejak 9.0 ini bahkan lebih mudah:

SELECT id, 
       string_agg(some_column, ',')
FROM the_table
GROUP BY id
seekor kuda tanpa nama
sumber
32
Perhatikan bahwa sintaks juga memungkinkan Anda untuk menentukan urutan nilai dalam string (atau array, menggunakan array_agg) misalnya string_agg(some_column, ',' ORDER BY some_column)atau bahkanstring_agg(surname || ', ' || forename, '; ' ORDER BY surname, forename)
IMSoP
8
Ini luar biasa yang distinctbekerja dengan string_agg, jadi orang dapat menggunakanstring_agg(distinct some_solumn, ',')
arun
3
Perhatikan bahwa Anda mungkin perlu memberikan nilai kolom ke TEXTjika itu adalah nilai yang tidak dapat dirangkai (mis. uuid). Ini akan terlihat sepertistring_agg(some_column::text, ',')
Kendall
48
SELECT array_to_string(array(SELECT a FROM b),', ');

Akan melakukannya juga.

genobis
sumber
Apakah mungkin untuk melakukan sesuatu seperti dalam komentar ini , di mana Anda mengumpulkan dalam urutan tertentu? Bagaimana Anda menangani pengelompokan berdasarkan satu kolom dan pemesanan dengan yang lain (misalnya, untuk menyatukan variabel dalam set data longitudinal)?
Michael A
15

Coba seperti ini:

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

dan versi untuk bekerja pada tipe array :

select
  array_to_string(
    array(select distinct unnest(zip_codes) from table),
    ', '
);
Sławomir Lenart
sumber
Jawaban rangkap, @max_spy mengatakan hal yang sama lima tahun lalu
Emil Vikström
@ EmilVikström: Anda berhak salah, tetapi baca dengan cermat. Ini tidak hanya berbeda, tetapi saya memberikan contoh, yang berfungsi dengan tipe array - seperti zip_codes character varying(5)[]. Juga, saya telah memverifikasi bahwa untuk tujuan saya - tidak diperlukan diperlukan, jika tidak Anda akan melihat ERROR: cannot accumulate arrays of different dimensionality.
Sławomir Lenart
1

Saran saya di 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
sumber
1
Mengapa Anda melakukan ORDER BYpermintaan dalam? Bukankah pemesanan akan hilang juga?
mypetlion
-1

Harapan di bawah permintaan Oracle akan berhasil.

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
sumber
Saya mengujinya di rextester.com/l/postgresql_online_compiler dan tidak berfungsi: 42883: function listagg (teks, tidak diketahui, teks) tidak ada
Manuel Romeiro
Oracle memiliki sintaks dan fungsi yang berbeda dari postgres.
Herman J. Radtke III