Équivalent Postgresql GROUP_CONCAT?


248

J'ai une table et je voudrais tirer une ligne par identifiant avec les valeurs de champ concaténées.

Dans ma table, par exemple, j'ai ceci:

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

Et je voudrais sortir:

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

Dans MySQL, j'ai pu utiliser la fonction d'agrégation GROUP_CONCAT, mais cela ne semble pas fonctionner ici ... Existe-t-il un équivalent pour PostgreSQL, ou une autre façon d'accomplir cela?





1
Je pense que la meilleure réponse est toujours dans une autre question: stackoverflow.com/a/47638417/243233
Jus12

Réponses:


237

C'est probablement un bon point de départ (version 8.4+ uniquement):

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

array_agg renvoie un tableau, mais vous pouvez le CASTER en texte et le modifier si nécessaire (voir les clarifications ci-dessous).

Avant la version 8.4, vous devez le définir vous-même avant d'utiliser:

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

(paraphrasé de la documentation PostgreSQL)

Clarifications:

  • Le résultat de la conversion d'un tableau en texte est que la chaîne résultante commence et se termine par des accolades. Ces accolades doivent être supprimées par une méthode, si elles ne sont pas souhaitées.
  • La conversion de ANYARRAY en TEXTE simule le mieux la sortie CSV car les éléments qui contiennent des virgules incorporées sont entre guillemets dans la sortie dans un style CSV standard. Ni array_to_string () ni string_agg () (la fonction "group_concat" ajoutée au 9.1) ne cite des chaînes avec des virgules incorporées, ce qui entraîne un nombre incorrect d'éléments dans la liste résultante.
  • La nouvelle fonction 9.1 string_agg () NE transforme PAS d'abord les résultats internes en TEXTE. Ainsi, "string_agg (value_field)" générerait une erreur si value_field est un entier. "string_agg (value_field :: text)" serait requis. La méthode array_agg () ne nécessite qu'une seule conversion après l'agrégation (plutôt qu'une conversion par valeur).

1
Et dans 9.0, vous aurez listagg ()
Scott Bailey

6
Pour obtenir CSV, la requête doit être: 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
Vous ne pouvez pas utiliser array_to_string dans tous les cas ici. Si votre champ value_field contient une virgule incorporée, le CSV résultant est incorrect. L'utilisation de array_agg () et le transtypage en TEXTE cite correctement les chaînes avec des virgules incorporées. La seule mise en garde est qu'il comprend également les accolades de début et de fin, d'où ma déclaration "et modifier au besoin". Je vais modifier pour clarifier ce point.
Matthew Wood

FYI: voici un lien vers les documents sur array_agg en 8.4
Michael Rusch

256

Depuis la version 9.0, c'est encore plus simple:

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

32
Notez que la syntaxe vous permet également de spécifier l'ordre des valeurs dans la chaîne (ou le tableau, en utilisant array_agg) par exemple string_agg(some_column, ',' ORDER BY some_column)ou mêmestring_agg(surname || ', ' || forename, '; ' ORDER BY surname, forename)
IMSoP

8
C'est génial qui distinctfonctionne avec string_agg, donc on peut utiliserstring_agg(distinct some_solumn, ',')
arun

3
Notez que vous devrez peut-être caster la valeur de la colonne TEXTs'il s'agit d'une valeur non chaîne (c'est-à-dire uuid). Cela ressemblerait àstring_agg(some_column::text, ',')
Kendall

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

Fera aussi bien.


Est-il possible de faire quelque chose comme dans ce commentaire , où vous agrégez dans un certain ordre? Comment géreriez-vous le regroupement par une colonne et le classement par une autre (par exemple, pour concaténer des variables dans un ensemble de données longitudinales)?
Michael A

15

Essayez comme ceci:

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

2

et la version pour travailler sur le type de tableau :

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

Réponse en double, @max_spy a dit la même chose il y a cinq ans
Emil Vikström

@ EmilVikström: vous avez raison de vous tromper, mais lisez attentivement. Ce n'est pas seulement différent, mais j'ai donné un exemple, qui fonctionne avec le type de tableau - comme zip_codes character varying(5)[]. De plus, j'ai vérifié que pour mon but - unnestr est nécessaire, sinon vous verrez ERROR: cannot accumulate arrays of different dimensionality.
Sławomir Lenart

1

Ma sugestion en 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
Pourquoi faites-vous ORDER BYdans une requête interne? La commande ne sera-t-elle pas perdue de toute façon?
mypetlion

-1

J'espère que la requête Oracle fonctionnera.

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

Je l'ai testé sur rextester.com/l/postgresql_online_compiler et n'ai pas fonctionné: 42883: la fonction listagg (texte, inconnu, texte) n'existe pas
Manuel Romeiro

Oracle a une syntaxe et des fonctions différentes de celles des postgres.
Herman J. Radtke III
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.