PostgreSQL: присоединение массивов в предложении group by
у нас есть проблема группировки массивов в один массив. Мы хотим присоединиться к значения из двух столбцов в один массив и совокупность этих массивов в несколько рядов.
учитывая следующие данные:
| id | name | col_1 | col_2 |
| 1 | a | 1 | 2 |
| 2 | a | 3 | 4 |
| 4 | b | 7 | 8 |
| 3 | b | 5 | 6 |
мы хотим следующий выход:
| a | { 1, 2, 3, 4 } |
| b | { 5, 6, 7, 8 } |
порядок элементов важен и должен коррелировать с идентификатором агрегированных строк.
мы попробовали функцию array_agg:
SELECT array_agg(ARRAY[col_1, col_2]) FROM mytable GROUP BY name;
к сожалению, это оператор вызывает ошибку:
ERROR: could not find array type for data type character varying[]
кажется невозможным объединить массивы в предложение group by с помощью array_agg.
какие идеи?
2 ответов
UNION ALL
вы можете "counter-pivot" с 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;
адаптировано для создания порядка значений, которые вы позже запросили. в документации:
агрегатные функции
array_agg
,json_agg
,string_agg
иxmlagg
, так же, как подобные определяемые пользователем агрегатные функции, производят значимо разные значения результата в зависимости от порядка входное значение. Этот заказ не определен по умолчанию, но может быть контролируется написаниемORDER BY
предложения в совокупности вызову показано в разделе 4.2.7. Альтернативно, поставляя входные значения от сортированный подзапрос обычно будет работать.
пользовательская агрегатная функция
или вы можете создать пользовательскую агрегатную функцию, как описано в этих связанных ответах:
выбор данных в массив 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
(который может быть добавлен к таким агрегатным функциям) гарантирует желаемый результат:
{1,2,3,4}
{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;
который производит 2-мерные массивы:
{{1,2},{3,4}}
{{5,6},{7,8}}
select n, array_agg(c) as c
from (
select n, unnest(array[c1, c2]) as c
from t
) s
group by n
или проще
select
n,
array_agg(c1) || array_agg(c2) as c
from t
group by n
для рассмотрения нового требования заказ:
select n, array_agg(c order by id, o) as c
from (
select
id, n,
unnest(array[c1, c2]) as c,
unnest(array[1, 2]) as o
from t
) s
group by n