我有一个数组作为一列的表,我想将数组元素加在一起:
> create table regres(a int[] not null);
> insert into regres values ('{1,2,3}'), ('{9, 12, 13}');
> select * from regres;
a
-----------
{1,2,3}
{9,12,13}
我希望结果是:
{10, 14, 16}
即:{1 + 9, 2 + 12, 3 + 13}
。
这样的功能在某处已经存在吗?插入扩展看起来是一个很好的候选者,但这样的功能还不存在。
数组的长度预计在24到31个元素之间,所有元素都是NOT NULL
,数组本身也总是NOT NULL
。所有元素都是基本的int
。每个聚合将有两行以上。在查询中,所有数组都将具有相同数量的元素。不同的查询将具有不同数量的元素。
我的实现目标是:x86_64-unknown-linux-gnu上的PostgreSQL 9.1.13,由gcc编译(Ubuntu / Linaro 4.6.3-1ubuntu5)4.6.3,64位
答案 0 :(得分:13)
Postgres 9.3 + 中的一般解决方案,适用于任意数量的元素阵列。
单个元素或整个数组也可以为NULL:
SELECT ARRAY (
SELECT sum(arr[rn])
FROM tbl t
, generate_subscripts(t.arr, 1) AS rn
GROUP BY rn
ORDER BY rn
);
这使用了隐含的LATERAL JOIN
(Postgres 9.3+)
使用您的示例值:
SELECT ARRAY (
SELECT sum(arr[rn])
FROM (
VALUES
('{1,2,3}'::int[])
,('{9,12,13}')
) t(arr)
, generate_subscripts(t.arr, 1) AS rn
GROUP BY rn
ORDER BY rn
);
非平凡的例子:
SELECT ARRAY (
SELECT sum(arr[rn])
FROM (
VALUES
('{1,2,3}'::int[])
,('{9,12,13}')
,('{1,1,1, 33}')
,('{NULL,NULL}')
,(NULL)
) t(arr)
, generate_subscripts(t.arr, 1) AS rn
GROUP BY rn
ORDER BY rn
);
WITH ORDINALITY
SELECT ARRAY (
SELECT sum(elem)
FROM tbl t
, unnest(t.arr) WITH ORDINALITY x(elem, rn)
GROUP BY rn
ORDER BY rn
)
SELECT ARRAY (
SELECT sum(arr[rn])
FROM (
SELECT arr, generate_subscripts(arr, 1) AS rn
FROM tbl t
) sub
GROUP BY rn
ORDER BY rn
);
在更高版本中也是如此,但SELECT
列表中的 set-returns函数不是标准SQL,而且有些人不赞成。因此,请使用上述替代品与当前的Postgres。
相关答案以及更多解释:
答案 1 :(得分:1)
我知道最初的问题和答案已经很旧了,但是对于发现此问题的其他人...我发现的最优雅、最灵活的解决方案是创建自定义聚合函数。如果您只需要单个结果数组,Erwin 的回答提供了一些很好的简单解决方案,但不会转换为可能包含其他表列和聚合的解决方案,例如在 GROUP BY
中。
使用自定义 array_add
函数和 array_sum
聚合函数:
CREATE OR REPLACE FUNCTION array_add(_a numeric[], _b numeric[])
RETURNS numeric[]
AS
$$
BEGIN
RETURN ARRAY(
SELECT coalesce(a, 0) + coalesce(b, 0)
FROM unnest(_a, _b) WITH ORDINALITY AS x(a, b, n)
ORDER BY n
);
END
$$ LANGUAGE plpgsql;
CREATE AGGREGATE array_sum(numeric[])
(
sfunc = array_add,
stype = numeric[],
initcond = '{}'
);
然后(使用您示例中的名称):
SELECT array_sum(a) a_sums
FROM regres;
返回您的总和数组,它也可以用于任何可以使用其他聚合函数的地方,所以如果您的表也有一个列 name
您想要分组,以及另一个数字数组,列b
:
SELECT name, array_sum(a) a_sums, array_sum(b) b_sums
FROM regres
GROUP BY name;
您不会获得从内置 sum
函数中获得的性能,只需选择 sum(a[1]), sum(a[2]), sum(a[3])
,您必须将 array_add
函数实现为一个编译的 C 函数来获得它。但是,如果您无法添加自定义 C 函数(例如托管云数据库,例如 AWS RDS),或者您没有聚合大量行,则可能不会注意到差异。>
答案 2 :(得分:0)
如果您需要更好的性能并可以安装Postgres扩展,则agg_for_vecs C扩展提供了vec_to_sum
函数,该函数应满足您的需求。它还提供了各种聚合函数,例如min
,max
,avg
和var_samp
,它们在数组而不是标量上运行。