PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Joindre des tableaux dans une clause group by

UNION ALL

Vous pouvez "contre-pivoter" avec UNION ALL d'abord :

SELECT name, array_agg(c) AS c_arr
FROM  (
   SELECT name, id, 1 AS rnk, col1 AS c FROM tbl
   UNION ALL
   SELECT name, id, 2, col2 FROM tbl
   ORDER  BY name, id, rnk
   ) sub
GROUP  BY 1;

Adapté pour produire l'ordre des valeurs que vous avez demandé ultérieurement. Le manuel :

J'insiste sur moi.

LATERAL sous-requête avec VALUES expression

LATERAL nécessite Postgres 9.3 ou plus tard.

SELECT t.name, array_agg(c) AS c_arr
FROM  (SELECT * FROM tbl ORDER BY name, id) t
CROSS  JOIN LATERAL (VALUES (t.col1), (t.col2)) v(c)
GROUP  BY 1;

Même résultat. Ne nécessite qu'un seul passage sur la table.

Fonction d'agrégation personnalisée

Ou vous pouvez créer une fonction d'agrégation personnalisée comme indiqué dans ces réponses associées :

CREATE AGGREGATE array_agg_mult (anyarray)  (
    SFUNC     = array_cat
  , STYPE     = anyarray
  , INITCOND  = '{}'
);

Ensuite, vous pouvez :

SELECT name, array_agg_mult(ARRAY[col1, col2] ORDER BY id) AS c_arr
FROM   tbl
GROUP  BY 1
ORDER  BY 1;

Ou, généralement plus rapide, bien qu'il ne s'agisse pas de SQL standard :

SELECT name, array_agg_mult(ARRAY[col1, col2]) AS c_arr
FROM  (SELECT * FROM tbl ORDER BY name, id) t
GROUP  BY 1;

L'identifiant ORDER BY id ajouté (qui peut être ajouté à ces fonctions d'agrégation) garantit le résultat souhaité :

a | {1,2,3,4}
b | {5,6,7,8}

Ou vous pourriez être intéressé par cette alternative :

SELECT name, array_agg_mult(ARRAY[ARRAY[col1, col2]] ORDER BY id) AS c_arr
FROM   tbl
GROUP  BY 1
ORDER  BY 1;

Qui produit des tableaux à 2 dimensions :

a | {{1,2},{3,4}}
b | {{5,6},{7,8}}

Le dernier peut être remplacé (et devrait l'être, car il est plus rapide !) par le array_agg() intégré dans Postgres 9.5 ou version ultérieure - avec sa capacité supplémentaire d'agrégation de tableaux :

SELECT name, array_agg(ARRAY[col1, col2] ORDER BY id) AS c_arr
FROM   tbl
GROUP  BY 1
ORDER  BY 1;

Même résultat. Le manuel :

Donc pas exactement la même chose que notre fonction d'agrégation personnalisée array_agg_mult();