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.
Depesz blog about jsonb.
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 1
You 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.
If you know the desired column names (be it the same or different from the keys in the JSON structure, you can use json[b]_to_recordset()
:
SELECT * FROM jsonb_to_recordset('[
{ "col1": "a", "col2": 1, "col3": 1, "col4": "one"},
{ "col1": "b", "col2": 2, "col3": 2, "col4": "two"},
{ "col1": "c", "col2": 3, "col3": 3, "col4": "three"},
{ "col1": "d", "col2": 4, "col3": 4, "col4": "four"}
]'::jsonb) AS t (col1 text, col2 integer, col3 integer, col4 text);
col1 │ col2 │ col3 │ col4
──────┼──────┼──────┼───────
a │ 1 │ 1 │ one
b │ 2 │ 2 │ two
c │ 3 │ 3 │ three
d │ 4 │ 4 │ four
As the documentation tells us,
Note: In json_populate_record
, json_populate_recordset
, json_to_record
and json_to_recordset
, type coercion from the JSON is "best effort" and may not result in desired values for some types. JSON keys are matched to identical column names in the target row type. JSON fields that do not appear in the target row type will be omitted from the output, and target columns that do not match any JSON field will simply be NULL
.
If you already have a table to work with, json_populate_recordset()
is an even better solution:
CREATE TABLE inputtable (col1 text, col2 integer, col3 integer, col4 text);
SELECT * FROM jsonb_populate_recordset(NULL::yourtable, '[
{ "col1": "a", "col2": 1, "col3": 1, "col4": "one"},
{ "col1": "b", "col2": 2, "col3": 2, "col4": "two"},
{ "col1": "c", "col2": 3, "col3": 3, "col4": "three"},
{ "col1": "d", "col2": 4, "col3": 4, "col4": "four"}
]'::jsonb);
col1 │ col2 │ col3 │ col4
──────┼──────┼──────┼───────
a │ 1 │ 1 │ one
b │ 2 │ 2 │ two
c │ 3 │ 3 │ three
d │ 4 │ 4 │ four
Now updating the table itself may be done like this:
WITH source AS (SELECT * FROM jsonb_populate_recordset [...])
UPDATE yourtable
SET col1 = s.col1, col2 = s.col2
FROM source AS s
WHERE col3 = s.col3;
In case it seems slow, it might make sense to not use the CTE, but a subquery in the FROM
clause instead.
Best Answer
Use the function
json_array_elements()
instead, available in Postgres 9.5.Quoting the manual, it ...
And values can in turn be JSON arrays (or any other JSON values).