PostgreSQL:在group by子句中连接数组

时间:2014-07-03 15:08:15

标签: sql arrays postgresql group-by postgresql-9.1

我们在将数组分组到单个数组时遇到了问题。 我们希望将两个列中的值连接到一个单独的数组中,并聚合这些多行的数组。

给出以下输入:

| 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 } |

元素的顺序很重要,应该与聚合行的id相关联。

我们尝试了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[]

似乎无法使用array_agg在group by子句中合并数组。

有什么想法吗?

2 个答案:

答案 0 :(得分:6)

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;

适合生成您稍后请求的值的顺序。 Per documentation:

  

汇总函数array_aggjson_aggstring_aggxmlagg,   以及类似的用户定义的聚合函数,生成   有意义的不同结果值取决于的顺序   输入值。默认情况下,此顺序未指定,但可以   通过在聚合调用中编写ORDER BY子句来控制,如   见4.2.7节。或者,提供输入值   排序的子查询通常会起作用。

自定义聚合函数

您可以创建自定义聚合函数,如以下相关答案中所述: Selecting data into a Postgres array
Is there something like a zip() function in PostgreSQL that combines two arrays?

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;

生成二维数组:

{{1,2},{3,4}}
{{5,6},{7,8}}

答案 1 :(得分:1)

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