PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Unindo arrays dentro do grupo por cláusula

UNION ALL


Você pode "contra-pivot" com UNION ALL primeiro:
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;

Adaptado para produzir a ordem de valores que você solicitou posteriormente. O manual:

Minha ênfase em negrito.

LATERAL subconsulta com VALUES expressão


LATERAL requer o Postgres 9.3 ou mais tarde.
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;

Mesmo resultado. Só precisa de uma única passagem sobre a mesa.

Função de agregação personalizada


Ou você pode criar uma função de agregação personalizada, conforme discutido nestas respostas relacionadas:
CREATE AGGREGATE array_agg_mult (anyarray)  (
    SFUNC     = array_cat
  , STYPE     = anyarray
  , INITCOND  = '{}'
);

Então você pode:
SELECT name, array_agg_mult(ARRAY[col1, col2] ORDER BY id) AS c_arr
FROM   tbl
GROUP  BY 1
ORDER  BY 1;

Ou, normalmente mais rápido, embora não seja SQL padrão:
SELECT name, array_agg_mult(ARRAY[col1, col2]) AS c_arr
FROM  (SELECT * FROM tbl ORDER BY name, id) t
GROUP  BY 1;

O ORDER BY id adicionado (que pode ser anexado a essas funções agregadas) garante o resultado desejado:
a | {1,2,3,4}
b | {5,6,7,8}

Ou você pode estar interessado nesta alternativa:
SELECT name, array_agg_mult(ARRAY[ARRAY[col1, col2]] ORDER BY id) AS c_arr
FROM   tbl
GROUP  BY 1
ORDER  BY 1;

Que produz arrays bidimensionais:
a | {{1,2},{3,4}}
b | {{5,6},{7,8}}

O último pode ser substituído (e deve ser, pois é mais rápido!) pelo array_agg() embutido no Postgres 9.5 ou posterior - com sua capacidade adicional de agregar arrays:
SELECT name, array_agg(ARRAY[col1, col2] ORDER BY id) AS c_arr
FROM   tbl
GROUP  BY 1
ORDER  BY 1;

Mesmo resultado. O manual:

Portanto, não é exatamente o mesmo que nossa função de agregação personalizada array_agg_mult();