UNION ALL
Podrías "contra-pivotar" con UNION ALL
primero:
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 producir el orden de valores que solicitó más tarde. El manual:
Énfasis en negrita mío.
LATERAL
subconsulta
con VALUES
expresión
LATERAL
requiere Postgres 9.3 o posterior.
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;
Mismo resultado. Solo necesita una sola pasada sobre la mesa.
Función agregada personalizada
O podría crear una función agregada personalizada como la discutida en estas respuestas relacionadas:
- Seleccionar datos en un Matriz Postgres
- ¿Existe algo como una función zip() en PostgreSQL que combine dos matrices?
CREATE AGGREGATE array_agg_mult (anyarray) (
SFUNC = array_cat
, STYPE = anyarray
, INITCOND = '{}'
);
Entonces puedes:
SELECT name, array_agg_mult(ARRAY[col1, col2] ORDER BY id) AS c_arr
FROM tbl
GROUP BY 1
ORDER BY 1;
O, por lo general, más rápido, aunque no SQL estándar:
SELECT name, array_agg_mult(ARRAY[col1, col2]) AS c_arr
FROM (SELECT * FROM tbl ORDER BY name, id) t
GROUP BY 1;
El ORDER BY id
agregado (que se puede agregar a dichas funciones agregadas) garantiza el resultado deseado:
a | {1,2,3,4}
b | {5,6,7,8}
O quizás te interese esta alternativa:
SELECT name, array_agg_mult(ARRAY[ARRAY[col1, col2]] ORDER BY id) AS c_arr
FROM tbl
GROUP BY 1
ORDER BY 1;
Lo que produce arreglos bidimensionales:
a | {{1,2},{3,4}}
b | {{5,6},{7,8}}
El último puede ser reemplazado (¡y debería serlo, ya que es más rápido!) con el array_agg()
incorporado en Postgres 9.5 o posterior, con su capacidad adicional de agregar matrices:
SELECT name, array_agg(ARRAY[col1, col2] ORDER BY id) AS c_arr
FROM tbl
GROUP BY 1
ORDER BY 1;
Mismo resultado. El manual:
Así que no es exactamente lo mismo que nuestra función agregada personalizada array_agg_mult()
;