Let's say I have a table in postgresql called 'Test' which contains a single column called 'Column1' of the type int[]. So, it can contain an array of integers for each row, for example, {1,2,3}. I want to know if it is possible to insert an array of arrays in a table in postgresql, like {{1,2,3},{1,3,5},{9,10}} in a single cell of a table in postgresql? If so then what should be the datatype of the column, and how can we achieve that?
Postgresql – Is possible to insert an array inside another array in postgresql
arraypostgresql
Related Solutions
jsonb
in Postgres 9.4 or later
Consider the jsonb
data type in Postgres 9.4 or later. The 'b' at the end stands for 'binary'. Among other things, there is an equality operator (=
) for jsonb
. Most people will want to switch.
json
There is no =
operator defined for the data type json
, because there is no well defined method to establish equality for whole json
values. But see below.
You could cast to text
and then use the =
operator. This is short, but only works if your text representation happens to match. Inherently unreliable, except for corner cases. See:
Or you can unnest
the array and use the ->>
operator to .. get JSON object field as text
and compare individual fields.
Test table
2 rows: first one like in the question, second one with simple values.
CREATE TABLE tbl (
tbl_id int PRIMARY KEY
, jar json[]
);
INSERT INTO t VALUES
(1, '{"{\"value\" : \"03334/254146\", \"typeId\" : \"ea4e7d7e-7b87-4628-ba50-f5\"}"
,"{\"value\" : \"03334/254147\", \"typeId\" : \"ea4e7d7e-7b87-4628-ba50-f6\"}"
,"{\"value\" : \"03334/254148\", \"typeId\" : \"ea4e7d7e-7b87-4628-ba50-f7\"}"}')
, (2, '{"{\"value\" : \"a\", \"typeId\" : \"x\"}"
,"{\"value\" : \"b\", \"typeId\" : \"y\"}"
,"{\"value\" : \"c\", \"typeId\" : \"z\"}"}');
Demos
Demo 1You could use array_remove()
with text
representations (unreliable).
SELECT tbl_id
, jar, array_length(jar, 1) AS jar_len
, jar::text[] AS t, array_length(jar::text[], 1) AS t_len
, array_remove(jar::text[], '{"value" : "03334/254147", "typeId" : "ea4e7d7e-7b87-4628-ba50-f6"}'::text) AS t_result
, array_remove(jar::text[], '{"value" : "03334/254147", "typeId" : "ea4e7d7e-7b87-4628-ba50-f6"}'::text)::json[] AS j_result
FROM tbl;
Demo 2
Unnest the array and test fields of individual elements.
SELECT tbl_id, array_agg(j) AS j_new
FROM tbl, unnest(jar) AS j -- LATERAL JOIN
WHERE j->>'value' <> '03334/254146'
AND j->>'typeId' <> 'ea4e7d7e-7b87-4628-ba50-6a5f6e63dbf5'
GROUP BY 1;
Demo 3
Alternative test with row type.
SELECT tbl_id, array_agg(j) AS j_new
FROM tbl, unnest(jar) AS j -- LATERAL JOIN
WHERE (j->>'value', j->>'typeId') NOT IN (
('03334/254146', 'ea4e7d7e-7b87-4628-ba50-6a5f6e63dbf5')
,('a', 'x')
)
GROUP BY 1;
UPDATE
as requested
Finally, this is how you could implement your UPDATE
:
UPDATE tbl t
SET jar = j.jar
FROM tbl t1
CROSS JOIN LATERAL (
SELECT ARRAY(
SELECT j
FROM unnest(t1.jar) AS j -- LATERAL JOIN
WHERE j->>'value' <> 'a'
AND j->>'typeId' <> 'x'
) AS jar
) j
WHERE t1.tbl_id = 2 -- only relevant rows
AND t1.tbl_id = t.tbl_id;
db<>fiddle here
About the implicit LATERAL JOIN
:
About unnesting arrays:
DB design
To simplify your situation consider an normalized schema: a separate table for the json
values (instead of the array column), joined in a n:1 relationship to the main table.
Order of source rows irrelevant, source is array column
Your updated requirements are much simpler for two reasons:
- We don't need to establish any order among source rows.
- And the array already establishes order among array elements.
Simple table:
CREATE TABLE vals (j int[]);
Simple query:
SELECT ARRAY(SELECT elem FROM vals, unnest(j) elem) AS arr;
Order of source rows relevant, source are integer columns
Not as simple. Based on this table:
CREATE TABLE vals (i int, j int);
There are various ways to preserve order. I think this should be among the fastest - with an ARRAY constructor:
SELECT ARRAY(
SELECT t.elem
FROM (SELECT i, j FROM vals ORDER BY i, j) v
, LATERAL (VALUES (v.i), (v.j)) t(elem)
) AS arr;
My first draft had ORDER BY i, j
after the LATERAL
join, which would not guarantee the order of columns. I pulled the ORDER BY
into a subselect to produce the correct order now. This works, but it's not documented. To be absolutely sure, you have to add an explicit ORDER BY
later:
SELECT ARRAY (
SELECT elem
FROM vals v, LATERAL (VALUES (1, v.i), (2, v.j)) t(ordr, elem)
ORDER BY v.i, v.j, ordr
) AS arr;
About guaranteed order:
About the VALUES
expression in a LATERAL
join:
Another way with - this time with array_agg()
:
SELECT array_agg(elem) AS arr
FROM (
SELECT i, j, 1 AS ordr, i AS elem FROM vals
UNION ALL
SELECT i, j, 2 AS ordr, j AS elem FROM vals
ORDER BY i, j, ordr
) sub;
The ARRAY constructor is faster, array_agg()
is easier to integrate with multiple result columns.
The query in your test case would actually work in Postgres 9.5 (currently beta, to be released in the course of 2015). Quoting the release notes for pg 9.5:
Allow
array_agg()
andARRAY()
to take arrays as inputs (Ali Akbar, Tom Lane)
However, you get a 2-dimenstional array instead, not your desired result. You can easily implement the same (but slower) aggregate function in older versions:
Related Question
- PostgreSQL – Storing Boolean Values as Array: Does It Make Sense?
- PostgreSQL – Using Multidimensional Arrays in Database Design
- Postgresql – Use an array returned from a subquery as argument in WHERE clause with ANY function in outer query
- Postgresql – Removing values from array in postgreSQL
- Postgresql – Using PostgreSQL to hold an array of data
- PostgreSQL – Fixing Malformed Array Literal in Composite Types
- PostgreSQL – Query Array Column with Array
- PostgreSQL – Calculating Average Value of JSONB Array
Best Answer
db<>fiddle here
Really - multi-dimentional array is formed.
No. 3rd element have 2 elements whereas first two have 3 elements. The error message
will be produced.
But different dimensions in different records are allowed.