Postgresql GROUP_CONCAT eşdeğeri?


248

Bir tablo var ve alan değerleri birleştirilmiş kimliği başına bir satır çekmek istiyorum.

Masamda, örneğin, ben var:

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

Ve çıktı almak istiyorum:

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

MySQL'de toplama işlevini kullanabildim GROUP_CONCAT, ancak burada işe yaramıyor ... PostgreSQL için bir eşdeğeri var mı yoksa bunu başarmanın başka bir yolu var mı?





1
En iyi cevabın hala başka bir soruda olduğunu düşünüyorum: stackoverflow.com/a/47638417/243233
Jus12

Yanıtlar:


237

Bu muhtemelen iyi bir başlangıç ​​noktasıdır (yalnızca sürüm 8.4+):

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

array_agg bir dizi döndürür, ancak bunu gerektiği gibi metin yazmak ve düzenlemek için CAST yapabilirsiniz (aşağıdaki açıklamalara bakın).

8.4 sürümünden önce, kullanmadan önce kendiniz tanımlamanız gerekir:

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

(PostgreSQL belgelerinden açıklanmıştır)

Açıklamalar:

  • Bir dizinin metne dökülmesinin sonucu, ortaya çıkan dizenin kıvırcık parantezlerle başlayıp bitmesidir. Bu parantezler istenmiyorsa bir yöntemle giderilmelidir.
  • ANYARRAY'ı TEXT'e yayınlamak, gömülü virgül içeren öğeler çıktıda standart CSV stilinde çift tırnak işareti bulunduğundan CSV çıktısını en iyi şekilde simüle eder. Array_to_string () veya string_agg () (9.1'de eklenen "group_concat" işlevi), katıştırılmış virgül içeren dizeleri alıntılayarak sonuçta ortaya çıkan listede yanlış sayıda öğeye neden olur.
  • Yeni 9.1 string_agg () işlevi, önce iç sonuçları TEXT'e VERMEZ. Değer_field bir tamsayı ise, "string_agg (value_field)" bir hata oluşturur. "string_agg (value_field :: text)" gerekli olacaktır. Array_agg () yöntemi, toplama işleminden sonra yalnızca bir döküm gerektirir (değer başına döküm yerine).

1
Ve 9.0'da listagg ()
Scott Bailey

6
CSV'yi almak için sorgu şöyle olmalıdır: SELECT id_field, array_to_string (dizi_agg (değer_field1), ','), dizi_to_string (dizi_agg (değer_field2), ',') 16_08'de data_table GROUP BY id_field
Nux

2
Burada tüm durumlarda array_to_string komutunu kullanamazsınız. Değer_ alanınız gömülü bir virgül içeriyorsa, elde edilen CSV yanlıştır. Array_agg () kullanılması ve TEXT öğesine döküm, katıştırılmış virgüllerle dizeleri düzgün şekilde tırnak içine alır. Tek uyarı, başlangıç ​​ve bitiş kıvırcık parantezleri içermesi, dolayısıyla ifademi "ve gerektiği gibi düzenleme" yi içermesidir. Bu noktayı açıklığa kavuşturmak için düzenleyeceğim.
Matthew Wood,


256

9.0'dan beri bu daha da kolay:

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

32
Sözdiziminin ayrıca dize (veya diziyi kullanarak array_agg) değer sırasını belirtmenize izin verdiğini unutmayın. Örneğin , string_agg(some_column, ',' ORDER BY some_column)hattastring_agg(surname || ', ' || forename, '; ' ORDER BY surname, forename)
IMSoP 16:13

8
distinctString_agg ile çalışan harika bir şey , bu yüzden bir tane kullanabilirsinizstring_agg(distinct some_solumn, ',')
arun

3
TEXTDize edilemeyen bir değer (ör. uuid) İse sütun değerini kullanmanız gerekebileceğini unutmayın . Bu benziyorstring_agg(some_column::text, ',')
Kendall

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

Yapacak da.


Belli bir sırayla topladığınız bu yorumdaki gibi bir şey yapmak mümkün mü ? Bir sütuna göre gruplamayı ve başka bir sütuna göre sıralamayı nasıl ele alırsınız (örneğin, uzunlamasına veri kümesindeki değişkenleri birleştirmek için)?
Michael A

15

Şunu deneyin:

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

2

ve dizi türünde çalışacak sürüm :

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

Yinelenen cevap, @max_spy beş yıl önce aynı şeyi söyledi
Emil Vikström

@ EmilVikström: yanılma hakkınız var ama dikkatlice okuyun. Sadece farklı değil, aynı zamanda dizi tipi ile çalışan bir örnek verdim - zip_codes olmak gibi character varying(5)[]. Ayrıca, benim amacım için - dürüstçe gerekli olduğunu doğruladım, aksi takdirde göreceksiniz ERROR: cannot accumulate arrays of different dimensionality.
Sławomir Lenart

1

Postgresql'deki önerim

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
Neden ORDER BYiçsel bir sorguda yapıyorsunuz ? Zaten sipariş kaybolmayacak mı?
mypetlion

-1

Oracle sorgusunun altındaki umut işe yarayacak.

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

Ben rextester.com/l/postgresql_online_compiler üzerinde test ve işe yaramadı: 42883: işlev listagg (metin, bilinmiyor, metin) mevcut değil
Manuel Romeiro

Oracle'ın postgres'den farklı sözdizimi ve işlevleri vardır.
Herman J. Radtke III
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.