PostgreSQL multidimensional arrays - arrays

I'm trying to pass data around as a multidimensional array, and I'm getting behavior that seems odd to me. Specifically I'm trying to get a single element out of a 2 dimensional array (so a 1 dimensional array out of my 2 dimension array), and it doesn't work the way I'd expect.
In the following examples #2, 4, & 5 work the way I'd expect, but 1 & 3 do not.
db=> select s.col[2] from (select array[[1,2,3],[4,5,6]] as col) s;
col
-----
(1 row)
db=> select s.col[2:2] from (select array[[1,2,3],[4,5,6]] as col) s;
col
-----
{{4,5,6}}
(1 row)
db=> select array[s.col[2]] from (select array[[1,2,3],[4,5,6]] as col) s;
array
--------
{NULL}
(1 row)
db=> select array[s.col[2:2]] from (select array[[1,2,3],[4,5,6]] as col) s;
array
-------------
{{{4,5,6}}}
(1 row)
db=> select s.col[2][1] from (select array[[1,2,3],[4,5,6]] as col) s;
col
-----
4
(1 row)
Is there doc on this? I have something that's working well enough for me right now, but it's ugly and I worry it won't do the things I want to do next. Technically I'm getting a 2 dimensional array, where 1 dimension only has 1 element. I'd rather just get an array.
I've read (among others):
http://www.postgresql.org/docs/9.1/static/arrays.html
http://www.postgresql.org/docs/9.1/static/functions-array.html
http://www.postgresql.org/docs/9.1/static/sql-expressions.html#SQL-SYNTAX-ARRAY-CONSTRUCTORS
And I'm just not seeing what I'm looking for.

Postgres array elements are always base elements, i.e. non-array types. Sub-arrays are not "elements" in Postgres. Array slices retain original dimensions.
You can either extract a base element, with element data type. Or you can extract an array slice, which retains the original array data type, and also original array dimensions.
Your idea to retrieve a sub-array as "element" would conflict with that and is just not implemented.
The manual might be made clearer in its explanation. But at least we can find:
If any dimension is written as a slice, i.e., contains a colon, then
all dimensions are treated as slices. Any dimension that has only a
single number (no colon) is treated as being from 1 to the number
specified. For example, [2] is treated as [1:2] ...
Your 1st example tries to reference a base element, which is not found (you'd need a subscript with two array indexes in a 2D array). So Postgres returns NULL.
Your 3rd example just wraps the resulting NULL in a new array.
To flatten an array slice (make it a 1D array) you can unnest() and feed the resulting set to a new ARRAY constructor. Either in a correlated subquery or in a LATERAL join (requires pg 9.3+). Demonstrating both:
SELECT s.col[2:2][2:3] AS slice_arr
, x.lateral_arr
, ARRAY(SELECT unnest(s.col[2:2][2:3])) AS corr_arr
FROM (SELECT ARRAY[[1,2,3],[4,5,6]] AS col) s
, LATERAL (
SELECT ARRAY(SELECT * FROM unnest(s.col[2:2][2:3])) AS lateral_arr
) x;
Be sure to read the current version of the manual. your references point to Postgres 9.1, but chances are you are actually using Postgres 9.4.
Related:
How to select 1d array from 2d array?
Unnest array by one level

Related

Mapping a 2D array into 1D array with variable column width

I know mapping 2D array into 1D array has been asked many times, but I did not find a solution that would fit a where the column count varies.
So I want get a 1-dimensional index from this 2-dimensional array
Col> _0____1____2__
Row 0 |_0__|_1__|_2__|
V 1 |_3__|_4__|
2 |_5__|_6__|_7__|
3 |_8__|_9__|
4 |_10_|_11_|_12_|
5 |_13_|_14_|
The normal formula index = row * columns + column does not work, since after the 2nd row the index is out of place.
What is the correct formula here?
EDIT:
The specific issue is that I have a list of items in with the layout like in the grid, but a one dimensional array for the data. So while looping through the elements in the UI, I need to get the correct data, but can only get the row and column for that element. I need to find a way to turn a row/column value into an index for the data-array
Bad picture trying to explain it
A truly optimal answer (or even a provably correct one) will depend on the language you are using and how it lays out memory for such arrays.
However, taking your question simply at face value, you have to know what the actual length of each row is in order to calculate a 1D index.
So either the row length follows some pattern that can be inferred from the data, or you have (or can write) a rlen = rowLength( 2dTable, RowNumber) function.
Then, depending on how big the tables are and how fast you need to run, you can calculate a 1D index from the 2d table by adding all the previous row lengths until the current row length is less than the 2d column index.
or build a 1d table of the row lengths (or commulative rowlengths) so you can scan it and so only call your rowlength function for each row only once.
With a better description of your problem, you might get a better answer...
For your example which alternates between 3 and 2 columns you can construct a formula:
index = (row / 2) * (3 + 2) + (row % 2 ? 3 : 0) + column
(C-like syntax, assuming integer division)
In general though, the one and only way to implement what you're doing here, jagged arrays, is to make an array of arrays, a.k.a. an Iliffe vector. That means, use the row number as index into an array of pointers which point to the individual row arrays containing the actual data.
You can have an additional 1D array having the length of the columns say "length". Then your formula is index=sum {length(i)}+column. i runs from 0 to row.

Accessing array-item from array of arrays

So simple... I need to retrieve an item of an array-of-arrays, so the item must be a simple array. Example:
WITH t AS (SELECT '{{1,2,3},{333,444,555}}'::int[][] as a, '{{{{1,2}}}}'::int[] as b)
SELECT
a[2], -- returns null (!), why not works?
a[2:2], -- returns array-into-array, not simple array
b[1][1], -- also null
b[1][1][1:2], -- another ugly array-into-array-into...-array
a[1][1] -- is correct, but only works with scalars
FROM t;
The result is NULL,{{333,444,555}},NULL,{{{{1,2}}}},1... I need a simple array for a[2], {333,444,555}... How to access it as it?
PS: the guide and google-examples only show slices... Perhaps it is so obvious but I not remember why a[2] is invalid in PostgreSQL.
I had forgotten what we discussed here: it is a restricted form of "array of arrays"... named "multidimensional array"... But I persisted in thinking in ambiguous way, that is, thinking sometimes in array of arrays...
The best answer comes from Tom Lane at the pgsql-hackers.at.postgresql.org forum, "Multi-dimensional arrays in Postgres are not 'arrays of arrays'".
So, in the "multidimensional array" world, not exist that kind of "access to an array-item" that I expected.
As #ViníciusGobboA.deOliveira commented here, the only solution is to write a function, through PL/SQL (low performance) or C (high performance).
a[2:2] will return integer[] type, while non-colon notation a[2] will return integer type, which in fact in your case should be an array. Based on the documentation:
An array slice is denoted by writing lower-bound:upper-bound for one
or more array dimensions.
Check the code below:
WITH test AS (
SELECT
'{
{11,12,13},
{21,22,23},
{31,32,33},
{41,42,43}
}'::INTEGER[][] as vv
)
SELECT
array_dims(vv) dimensions,-- [1:4][1:3] - get all dimensions
vv[1:4] all_slices, -- entire 2d-array
vv[2:3] second_and_third_slices, -- {{21,22,23},{{31,32,33}}
vv[2:2] second_slice, -- {{21,22,23}} - get specific slice
pg_typeof(vv[2:2]) second_slice_type, -- type of slice above is INTEGER[]!
pg_typeof(vv[2]) vv_type, -- type is INTEGER
vv[2:2][2:2] second_slice_second_element, -- 22 - get specific element
vv[2:2][2:3] second_slice_second_and_third_elements -- {{22,23}}
FROM test;

How do I select a subset of an array in Postgres?

Assuming I have an array in one of my postgres columns:
> select array[1,2,3,4];
array
-----------
{1,2,3,4}
How do I select a subset of the items from that array, in general? For instance, if I want to select items from the x index to the y index, how would I grab the items in that range (x to y)?
I'm using PostgreSQL 9.4.
From the fine manual:
8.15.3. Accessing Arrays
[...]
We can also access arbitrary rectangular slices of an array, or subarrays. An array slice is denoted by writing lower-bound:upper-bound for one or more array dimensions.
[...]
It is possible to omit the lower-bound and/or upper-bound of a slice specifier; the missing bound is replaced by the lower or upper limit of the array's subscripts.
For example:
=> select (array[1,2,3,4,5,6])[2:5];
array
-----------
{2,3,4,5}
(1 row)
=> select (array[1,2,3,4,5,6])[:5];
array
-------------
{1,2,3,4,5}
(1 row)
=> select (array[1,2,3,4,5,6])[2:];
array
-------------
{2,3,4,5,6}
(1 row)
=> select (array[1,2,3,4,5,6])[:];
array
---------------
{1,2,3,4,5,6}
(1 row)
So to get a slice from index x to y (inclusive), you'd say:
array_column[x:y]

Aggregate values by row count

I have a series of rows and I need to aggregate values from these rows into the groups of N elements, accumulating values from current and N-1 succeeding rows.
With N=3 and data being:
VALUES (1),(2),(3),(4),(5);
I want to receive the following set of rows (arrays):
{1,2,3}
{2,3,4}
{3,4,5}
{4,5}
{5}
It is important, that N is a variable, so I cannot use joins.
Well, this can be solved using frames together with window functions.
The question in subject can be solved like this:
WITH v(v) AS (VALUES (1),(2),(3),(4),(5))
SELECT v,
array_agg(v) OVER (ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) AS arr
FROM v;
And the following example illustrates how to get a list of complete arrays, i.e. eliminate those that don't contain all N entries:
WITH cnt(c) AS (SELECT 3),
val(v) AS (VALUES (1),(2),(3),(4),(5)),
arr AS
(SELECT v,
array_agg(v) OVER (ROWS BETWEEN CURRENT ROW
AND (SELECT c-1 FROM cnt) FOLLOWING) AS arr
FROM val)
SELECT v,arr
FROM arr
WHERE array_upper(arr,1) = (SELECT c FROM cnt);
I really love window functions!

How to get the last element of an array in PostgreSQL?

The PostgreSQL Documentation on arrays provides an example using [-1] to access what appears to be the last element of an array; however while SELECT arr[2:3]; produces {5,9}, arr[2:-1] results in {}.
How can the last element of an array be obtained in PostgreSQL?
Edit: Windows, PostgreSQL v9.2.1
For any array "arr", to fetch the last element of array arr use
SELECT arr[array_upper(arr, 1)];
I think you're misinterpreting the example. PostgreSQL arrays don't have to be indexed from 1 to n, that's just the default:
By default PostgreSQL uses a one-based numbering convention for arrays, that is, an array of n elements starts with array[1] and ends with array[n].
The example you're looking at is this:
SELECT f1[1][-2][3] AS e1, f1[1][-1][5] AS e2
FROM (SELECT '[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}'::int[] AS f1) AS ss;
But those negative numbers aren't indexing from the end of the arrays as in languages such as Perl. In the FROM (SELECT ... part, they're specifying the starting and ending indexes so the -1 in f1[1][-1][5] is just a plain old index. Consider this array_dims result:
=> SELECT array_dims('[1:1][-2:-1][3:5]={{{1,2,3},{4,5,6}}}'::int[]);
array_dims
-------------------
[1:1][-2:-1][3:5]
If you're using the default 1-based arrays then you can get the last element with a simple arr[array_length(arr, 1)]. If you're not using the default [1:n] arrays then you'll have to mess around with array_lower and array_upper to get the first and last elements; or, depending on the circumstances, you might be able to use unnest to unpack the array then work with the array as a rowset.
If someone is using Postgre 9.5, the documentation says:
-> int
Get JSON array element (indexed from zero, negative integers count from the end)
So this works for me:
to_json(arr)->-1

Resources