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?





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).

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


256

Sejak 9.0 ini bahkan lebih mudah:

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

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.


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;

2

dan versi untuk bekerja pada tipe array :

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

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   

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

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.