I want to take an array of n
dimensions and return set containing rows of arrays of n-1
dimensions. For example, take the array ARRAY[[1,2,3]
SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[0]
returns the same as
SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[17]
which is NULL. I quote the docs on that matter:
By default, the lower bound index value of an array's dimensions is set to one.
0
has no special meaning here.
Also, with a two-dimensional arrays, you need two indexes to get a base element. Like this:
SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[1][2]
Result:
2
The first part of your message is a bit unclear.
SELECT array_dims(ARRAY[[1,2,3], [4,5,6], [7,8,9]])
Result:
[1:3][1:3]
That's two dimensions with 3 elements (1 to 3) each (9 base elements).
If you want n-1
dimensions then this is a correct result:
SELECT ARRAY (SELECT unnest('{{1,2,3}, {4,5,6}, {7,8,9}}'::int[]))
Result:
{1,2,3,4,5,6,7,8,9}
That's one dimension. unnest()
always produces one base element per row. I am not sure what result you desire exactly . Your example is just another 2-dimenstional array with a missing set of curly brackets ... ?
{1,2,3}, {4,5,6}, {7,8,9}
If you want a slice of the array, try this notation:
SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[1:2]
Result:
{{1,2,3},{4,5,6}}
Or this:
SELECT (ARRAY[[1,2,3], [4,5,6], [7,8,9]])[2:2][1:2]
Result:
{{4,5}}
To flatten the result (get a 1D array):
Read more in the manual here.
Later test revealed that this plpgsql function is much faster. Requires Postgres 9.1 or later:
CREATE OR REPLACE FUNCTION unnest_2d_1d(ANYARRAY, OUT a ANYARRAY)
RETURNS SETOF ANYARRAY AS
$func$
BEGIN
FOREACH a SLICE 1 IN ARRAY $1 LOOP
RETURN NEXT;
END LOOP;
END
$func$ LANGUAGE plpgsql IMMUTABLE;
See:
This is an improved and simplified version of the function Lukas posted:
CREATE OR REPLACE FUNCTION unnest_2d_1d(anyarray)
RETURNS SETOF anyarray AS
$func$
SELECT array_agg($1[d1][d2])
FROM generate_subscripts($1,1) d1
, generate_subscripts($1,2) d2
GROUP BY d1
ORDER BY d1
$func$ LANGUAGE sql IMMUTABLE;
For Postgres versions < 8.4, array_agg()
is not installed by default. Create it first:
CREATE AGGREGATE array_agg(anyelement) (
SFUNC=array_append,
STYPE=anyarray,
INITCOND='{}'
);
Also, generate_subscripts() is not born, yet. Use instead:
...
FROM generate_series(array_lower($1,1), array_upper($1,1)) d1
, generate_series(array_lower($1,2), array_upper($1,2)) d2
...
Call:
SELECT unnest_2d_1d(ARRAY[[1,2], [3,4], [5,6]]);
Result
{1,2}
{3,4}
{5,6}
SQL Fiddle.
Slices of a multi-dimensional are returned as multi-dimensional arrays. This is a modified version of unnest that will take a 2-dimensional array and return a set of 1-dimensional arrays.
update: modified to use the built-in array_agg aggregate function that was default as of 8.4. (http://www.postgresql.org/docs/9.2/static/functions-aggregate.html)
Caveats:
code:
CREATE OR REPLACE FUNCTION unnest_multidim(anyarray)
RETURNS SETOF anyarray AS
$BODY$
SELECT array_agg($1[series2.i][series2.x]) FROM
(SELECT generate_series(array_lower($1,2),array_upper($1,2)) as x, series1.i
FROM
(SELECT generate_series(array_lower($1,1),array_upper($1,1)) as i) series1
) series2
GROUP BY series2.i
$BODY$
LANGUAGE sql IMMUTABLE;
Result:
select unnest_multidim(array[[1,2,3],[4,5,6],[7,8,9]]);
unnest_multidim
----------------------
{1,2,3}
{4,5,6}
{7,8,9}
(3 rows)
Now, let's say for some reason you want easy access to just one of these arrays that is returned. The following function adds an optional index parameter that will return the nested array of the index you provide, or, if you provide null, will output the full set of "unnested" arrays.
CREATE OR REPLACE FUNCTION unnest_multidim(anyarray, integer)
RETURNS SETOF anyarray AS
$BODY$
SELECT array_agg($1[series2.i][series2.x]) FROM
(SELECT generate_series(array_lower($1,2),array_upper($1,2)) as x, series1.i
FROM
(SELECT CASE WHEN $2 IS NULL THEN
generate_series(array_lower($1,1),array_upper($1,1))
ELSE $2
END as i) series1
) series2
GROUP BY series2.i
$BODY$
LANGUAGE sql IMMUTABLE;
Results:
db=> select unnest_multidim(array[[1,2,3],[4,5,6],[7,8,9]],2);
unnest_multidim
-----------------
{4,5,6}
(1 row)
db=> select unnest_multidim(array[[1,2,3],[4,5,6],[7,8,9]],NULL);
unnest_multidim
-----------------
{1,2,3}
{4,5,6}
{7,8,9}
(3 rows)
A word of caution: when using array_agg on postgres <9 order may change PostgreSQL array_agg order If you plan to use the unnested array say for finding argmax, this will corrupt your data.