10

I have a table like this;

CREATE TABLE test (
  id BIGSERIAL PRIMARY KEY,
  data JSONB
);

INSERT INTO test(data) VALUES('[1,2,"a",4,"8",6]'); -- id = 1
INSERT INTO test(data) VALUES('[1,2,"b",4,"7",6]'); -- id = 2

How to update element data->1 and data->3 into something else without PL/*?

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Kokizzu
  • 24,974
  • 37
  • 137
  • 233

2 Answers2

7

For Postgres 9.5 or later use jsonb_set(). See later answer of adriaan.


You cannot manipulate selected elements of a json / jsonb type directly. Functionality for that is still missing in Postgres 9.4. You have to do 3 steps:

  1. Unnest / decompose the JSON value.
  2. Manipulate selected elements.
  3. Aggregate / compose the value back again.

To replace the 3rd element of the json array (data->3) in the row with id = 1 with a given (new) value ('<new_value>'):

UPDATE test t
SET    data = t2.data
FROM  (
   SELECT id, array_to_json(
                 array_agg(CASE WHEN rn = 1 THEN '<new_value>' ELSE elem END))
              ) AS data
   FROM   test t2
        , json_array_elements_text(t2.data) WITH ORDINALITY x(elem, rn)         
   WHERE  id = 1
   GROUP  BY 1
   ) t2
WHERE  t.id = t2.id
AND    t.data <> t2.data; -- avoid empty updates

About json_array_elements_text():

About WITH ORDINALITY:

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • 3
    Functions to transform jsonb values should be coming in 9.5. There's nothing inherently different to any kind of other function that takes inputs and produces outputs. It's mostly that nobody's implemented functions to replace elements, etc, yet. – Craig Ringer Dec 09 '14 at 13:40
5

You can do this from PostgreSQL 9.5 with jsonb_set:

INSERT INTO test(data) VALUES('[1,2,"a",4,"8",6]');
UPDATE test SET data = jsonb_set(data, '{2}','"b"', false) WHERE id = 1

Try it out with a simple select:

SELECT jsonb_set('[1,2,"a",4,"8",6]', '{2}','"b"', false)
-- [1, 2, "b", 4, "8", 6]

And if you want to update two fields you can do:

SELECT jsonb_set(jsonb_set('[1,2,"a",4,"8",6]', '{0}','100', false), '{2}','"b"', false)
-- [100, 2, "b", 4, "8", 6]
adriaan
  • 1,088
  • 1
  • 12
  • 29