Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initial array in function to aggregate multi-dimensional array

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?

like image 298
finlayt Avatar asked Mar 23 '12 01:03

finlayt


People also ask

How initial values can be assigned to a multidimensional array?

You can initialize a multidimensional array using any of the following techniques: Listing the values of all elements you want to initialize, in the order that the compiler assigns the values. The compiler assigns values by increasing the subscript of the last dimension fastest.

How do you declare and initialize a multidimensional array explain by example?

Like the one-dimensional arrays, two-dimensional arrays may be initialized by following their declaration with a list of initial values enclosed in braces. Ex: int a[2][3]={0,0,0,1,1,1}; initializes the elements of the first row to zero and the second row to one. The initialization is done row by row.

How do you initialize a multidimensional array in Java?

Here is how we can initialize a 2-dimensional array in Java. int[][] a = { {1, 2, 3}, {4, 5, 6, 9}, {7}, }; As we can see, each element of the multidimensional array is an array itself. And also, unlike C/C++, each row of the multidimensional array in Java can be of different lengths.

How do you calculate multidimensional array?

The total number of elements that can be stored in a multidimensional array can be calculated by multiplying the size of all the dimensions. For example: The array int x[10][20] can store total (10*20) = 200 elements. Similarly array int x[5][10][20] can store total (5*10*20) = 1000 elements.


2 Answers

Postgres 9.5 or newer

... ships with an additional variant of the aggregate function array_agg(). The manual:

input arrays concatenated into array of one higher dimension (inputs must all have same dimensionality, and cannot be empty or null)

So not exactly the same as the custom aggregate function array_agg_mult() below. But use it, if you can. It's faster.

Related:

  • How to sort two dimensional int array in PostgreSQL?

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 arr1  --> 1-dim array
     , array_agg_mult(ARRAY[arr1])        AS arr2  --> 2-dim array
     , array_agg_mult(ARRAY[ARRAY[arr1]]) AS arr3  --> 3-dim array
       -- etc.
FROM  (
   VALUES
      ('{1,2,3}'::int[])                           -- 1-dim array
    , ('{4,5,6}')
    , ('{7,8,9}')
   ) t(arr1);

Or:

SELECT array_agg_mult(arr2)        AS arr2  --> 2-dim array
     , array_agg_mult(ARRAY[arr2]) AS arr3  --> 3-dim array
     , array_agg(arr2)             AS arr3  --> 3-dim array; superior in Postgres 9.5+
FROM  (
   VALUES
      ('{{1,2,3}}'::int[])                  -- 2-dim 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).

like image 140
Erwin Brandstetter Avatar answered Sep 26 '22 03:09

Erwin Brandstetter


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}}
like image 21
Lukas Eklund Avatar answered Sep 25 '22 03:09

Lukas Eklund