UNION ALL
Бихте могли да "контра-въртите" с UNION ALL
първо:
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;
Адаптиран за създаване на реда на стойностите, които сте поискали по-късно. Ръководството:
Удебелен акцент е мой.
LATERAL
подзаявка
с VALUES
израз
LATERAL
изисква Postgres 9.3 или по-късно.
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;
Същият резултат. Нуждае се само от едно минаване над масата.
Персонализирана агрегатна функция
Или можете да създадете персонализирана агрегатна функция, както е обсъдено в тези свързани отговори:
- Избиране на данни в Postgres масив
- Има ли нещо като функция zip() в PostgreSQL, която комбинира два масива?
CREATE AGGREGATE array_agg_mult (anyarray) (
SFUNC = array_cat
, STYPE = anyarray
, INITCOND = '{}'
);
След това можете:
SELECT name, array_agg_mult(ARRAY[col1, col2] ORDER BY id) AS c_arr
FROM tbl
GROUP BY 1
ORDER BY 1;
Или обикновено по-бързо, но не е стандартен SQL:
SELECT name, array_agg_mult(ARRAY[col1, col2]) AS c_arr
FROM (SELECT * FROM tbl ORDER BY name, id) t
GROUP BY 1;
Добавеният ORDER BY id
(който може да бъде добавен към такива агрегатни функции) гарантира желания резултат:
a | {1,2,3,4}
b | {5,6,7,8}
Или може да се интересувате от тази алтернатива:
SELECT name, array_agg_mult(ARRAY[ARRAY[col1, col2]] ORDER BY id) AS c_arr
FROM tbl
GROUP BY 1
ORDER BY 1;
Което създава двумерни масиви:
a | {{1,2},{3,4}}
b | {{5,6},{7,8}}
Последният може да бъде заменен (и трябва да бъде, тъй като е по-бърз!) с вградения array_agg()
в Postgres 9.5 или по-късно - с добавената възможност за агрегиране на масиви:
SELECT name, array_agg(ARRAY[col1, col2] ORDER BY id) AS c_arr
FROM tbl
GROUP BY 1
ORDER BY 1;
Същият резултат. Ръководството:
Така че не е точно същото като нашата персонализирана агрегатна функция array_agg_mult()
;