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