Initial array in function to aggregate multi-dimensional array

我的未来我决定 提交于 2019-11-26 16:38:29

问题


I have a table with arrays of integer.

I want to create an aggregate function that will return a 2-dimensional array with all the rows together. It then gets passed to plr to do some maths on it.

I have:

CREATE OR REPLACE
FUNCTION arrayappend(left int[][], right int[]) 
RETURNS int[] AS 
$BODY$
   SELECT $1 || $2 ;
$BODY$
LANGUAGE SQL;

and:

CREATE AGGREGATE array_sum2 (int[])  (
    SFUNC     = arrayappend,
    STYPE     = int[][],
    INITCOND  = '{}'
);

But the return type is int[], not int[][]?

How can I initialize the aggregate with an empty two dimensional array of integer?


回答1:


Postgres 9.5 or newer

... ships with an additional variant of the aggregate function array_agg() that can aggregate arrays into an array of the next higher dimension. See:

  • How to sort two dimensional int array in PostgreSQL?

It serves as drop-in replacement for the custom aggregate function array_agg_mult() below.

Postgres 9.4 or older

Aggregate function for any array type

With the polymorphic type anyarray it works for all kinds of arrays (including integer[]):

CREATE AGGREGATE array_agg_mult (anyarray) (
   SFUNC     = array_cat
 , STYPE     = anyarray
 , INITCOND  = '{}'
);

As @Lukas provided, the custom function arrayappend() is not needed. The built in array_cat() does the job. However, that doesn't explain why your example fails, while the one in @Lukas' answer works. The relevant difference is that @Lukas nested the array into another array layer with array[d.a].

You trip over the incorrect assumption that you could declare a type int[][]. But you cannot: int[][] is the same type as int[] for the PostgreSQL type system. The chapter on array types in the manual explains:

The current implementation does not enforce the declared number of dimensions either. Arrays of a particular element type are all considered to be of the same type, regardless of size or number of dimensions. So, declaring the array size or number of dimensions in CREATE TABLE is simply documentation; it does not affect run-time behavior.

An n-dimensional integer array effectively is an array of n-1-dimensional arrays of integer in PostgreSQL. You can't tell that from the type which only defines the base element. You have to ask array_dims() to get the specifics.

To demonstrate:

SELECT array_agg_mult(arr1)               AS arr2  --> 2-dimensional array
     , array_agg_mult(ARRAY[arr1])        AS arr3  --> 3-dimensional array
     , array_agg_mult(ARRAY[ARRAY[arr1]]) AS arr4  --> 4-dimensional array
       -- etc.
FROM  (
   VALUES
      ('{1,2,3}'::int[])                          -- = 1-dimensional array
    , ('{4,5,6}')
    , ('{7,8,9}')
   ) t(arr1);

Or:

SELECT array_agg(arr2) AS arr3  --> 3-dimensional array
FROM  (
   VALUES
      ('{{1,2,3}}'::int[])      -- = 2-dimensional array
     ,('{{4,5,6}}')
     ,('{{7,8,9}}')
   ) t(arr2);

All resulting columns are of the same type: int[] (even though containing a different number of dimensions).




回答2:


Using the built-in array_cat function works.

CREATE AGGREGATE array_sum2 (int[])  (
    SFUNC     = array_cat,
    STYPE     = int[],
    INITCOND  = '{}'
);

test:

select array_sum2(array[d.a]) from (select array[1,1,2,3] as a union select array[5,8,13,21] as a) d;
       array_sum2        
-------------------------
 {{1,1,2,3},{5,8,13,21}}


来源:https://stackoverflow.com/questions/9832973/initial-array-in-function-to-aggregate-multi-dimensional-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!