Bagaimana cara memodifikasi bidang di dalam tipe data JSON PostgreSQL baru?

235

Dengan postgresql 9.3 saya bisa SELECT bidang tertentu dari tipe data JSON, tetapi bagaimana Anda memodifikasinya menggunakan UPDATE? Saya tidak dapat menemukan contoh ini di dokumentasi postgresql, atau di mana pun secara online. Saya sudah mencoba yang jelas:

postgres=# create table test (data json);
CREATE TABLE
postgres=# insert into test (data) values ('{"a":1,"b":2}');
INSERT 0 1
postgres=# select data->'a' from test where data->>'b' = '2';
 ?column?
----------
 1
(1 row)
postgres=# update test set data->'a' = to_json(5) where data->>'b' = '2';
ERROR:  syntax error at or near "->"
LINE 1: update test set data->'a' = to_json(5) where data->>'b' = '2...
pengguna9645
sumber

Jawaban:

331

Pembaruan : Dengan PostgreSQL 9.5 , ada beberapa jsonbfungsi manipulasi di dalam PostgreSQL itu sendiri (tetapi tidak ada untuk json; pemeran diperlukan untuk memanipulasi jsonnilai).

Menggabungkan 2 (atau lebih) objek JSON (atau array gabungan):

SELECT jsonb '{"a":1}' || jsonb '{"b":2}', -- will yield jsonb '{"a":1,"b":2}'
       jsonb '["a",1]' || jsonb '["b",2]'  -- will yield jsonb '["a",1,"b",2]'

Jadi, pengaturan kunci sederhana dapat dilakukan dengan menggunakan:

SELECT jsonb '{"a":1}' || jsonb_build_object('<key>', '<value>')

Di mana <key>harus string, dan <value>bisa apa pun jenis to_jsonb()menerima.

Untuk mengatur nilai jauh di dalam hirarki JSON , jsonb_set()fungsi tersebut dapat digunakan:

SELECT jsonb_set('{"a":[null,{"b":[]}]}', '{a,1,b,0}', jsonb '{"c":3}')
-- will yield jsonb '{"a":[null,{"b":[{"c":3}]}]}'

Daftar parameter lengkap jsonb_set():

jsonb_set(target         jsonb,
          path           text[],
          new_value      jsonb,
          create_missing boolean default true)

pathjuga bisa memuat indeks array JSON & bilangan bulat negatif yang muncul di sana terhitung dari akhir array JSON. Namun, indeks array JSON yang tidak ada, tetapi positif akan menambahkan elemen ke akhir array:

SELECT jsonb_set('{"a":[null,{"b":[1,2]}]}', '{a,1,b,1000}', jsonb '3', true)
-- will yield jsonb '{"a":[null,{"b":[1,2,3]}]}'

Untuk memasukkan ke dalam array JSON (sambil mempertahankan semua nilai asli) , jsonb_insert()fungsi tersebut dapat digunakan ( dalam 9.6+; fungsi ini saja, di bagian ini ):

SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b,0}', jsonb '2')
-- will yield jsonb '{"a":[null,{"b":[2,1]}]}', and
SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b,0}', jsonb '2', true)
-- will yield jsonb '{"a":[null,{"b":[1,2]}]}'

Daftar parameter lengkap jsonb_insert():

jsonb_insert(target       jsonb,
             path         text[],
             new_value    jsonb,
             insert_after boolean default false)

Sekali lagi, bilangan bulat negatif yang muncul dalam pathhitungan dari akhir array JSON.

Jadi, selanjutnya menambahkan ke akhir array JSON dapat dilakukan dengan:

SELECT jsonb_insert('{"a":[null,{"b":[1,2]}]}', '{a,1,b,-1}', jsonb '3', true)
-- will yield jsonb '{"a":[null,{"b":[1,2,3]}]}', and

Namun, fungsi ini bekerja sedikit berbeda (dari jsonb_set()) ketika pathin targetadalah kunci objek JSON. Dalam hal ini, itu hanya akan menambahkan pasangan nilai kunci baru untuk objek JSON ketika kunci tidak digunakan. Jika digunakan, itu akan menimbulkan kesalahan:

SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,c}', jsonb '[2]')
-- will yield jsonb '{"a":[null,{"b":[1],"c":[2]}]}', but
SELECT jsonb_insert('{"a":[null,{"b":[1]}]}', '{a,1,b}', jsonb '[2]')
-- will raise SQLSTATE 22023 (invalid_parameter_value): cannot replace existing key

Menghapus kunci (atau indeks) dari objek JSON (atau, dari array) dapat dilakukan dengan -operator:

SELECT jsonb '{"a":1,"b":2}' - 'a', -- will yield jsonb '{"b":2}'
       jsonb '["a",1,"b",2]' - 1    -- will yield jsonb '["a","b",2]'

Menghapus, dari dalam hierarki JSON dapat dilakukan dengan #-operator:

SELECT '{"a":[null,{"b":[3.14]}]}' #- '{a,1,b,0}'
-- will yield jsonb '{"a":[null,{"b":[]}]}'

Untuk 9.4 , Anda dapat menggunakan versi modifikasi dari jawaban asli (di bawah), tetapi alih-alih menggabungkan string JSON, Anda dapat menggabungkan ke dalam objek json secara langsung json_object_agg().

Jawaban asli : Dimungkinkan (tanpa plpython atau plv8) dalam SQL murni juga (tetapi perlu 9.3+, tidak akan bekerja dengan 9.2)

CREATE OR REPLACE FUNCTION "json_object_set_key"(
  "json"          json,
  "key_to_set"    TEXT,
  "value_to_set"  anyelement
)
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')::json
  FROM (SELECT *
          FROM json_each("json")
         WHERE "key" <> "key_to_set"
         UNION ALL
        SELECT "key_to_set", to_json("value_to_set")) AS "fields"
$function$;

SQLFiddle

Edit :

Versi, yang menetapkan beberapa kunci & nilai:

CREATE OR REPLACE FUNCTION "json_object_set_keys"(
  "json"          json,
  "keys_to_set"   TEXT[],
  "values_to_set" anyarray
)
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')::json
  FROM (SELECT *
          FROM json_each("json")
         WHERE "key" <> ALL ("keys_to_set")
         UNION ALL
        SELECT DISTINCT ON ("keys_to_set"["index"])
               "keys_to_set"["index"],
               CASE
                 WHEN "values_to_set"["index"] IS NULL THEN 'null'::json
                 ELSE to_json("values_to_set"["index"])
               END
          FROM generate_subscripts("keys_to_set", 1) AS "keys"("index")
          JOIN generate_subscripts("values_to_set", 1) AS "values"("index")
         USING ("index")) AS "fields"
$function$;

Sunting 2 : seperti @ErwinBrandstetter mencatat fungsi-fungsi di atas berfungsi seperti yang disebut UPSERT(memperbarui bidang jika ada, menyisipkan jika tidak ada). Berikut ini varian, yang hanya UPDATE:

CREATE OR REPLACE FUNCTION "json_object_update_key"(
  "json"          json,
  "key_to_set"    TEXT,
  "value_to_set"  anyelement
)
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT CASE
  WHEN ("json" -> "key_to_set") IS NULL THEN "json"
  ELSE (SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')
          FROM (SELECT *
                  FROM json_each("json")
                 WHERE "key" <> "key_to_set"
                 UNION ALL
                SELECT "key_to_set", to_json("value_to_set")) AS "fields")::json
END
$function$;

Sunting 3 : Berikut adalah varian rekursif, yang dapat mengatur ( UPSERT) nilai daun (dan menggunakan fungsi pertama dari jawaban ini), yang terletak di jalur-kunci (di mana kunci hanya bisa merujuk ke objek dalam, array dalam tidak didukung):

CREATE OR REPLACE FUNCTION "json_object_set_path"(
  "json"          json,
  "key_path"      TEXT[],
  "value_to_set"  anyelement
)
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT CASE COALESCE(array_length("key_path", 1), 0)
         WHEN 0 THEN to_json("value_to_set")
         WHEN 1 THEN "json_object_set_key"("json", "key_path"[l], "value_to_set")
         ELSE "json_object_set_key"(
           "json",
           "key_path"[l],
           "json_object_set_path"(
             COALESCE(NULLIF(("json" -> "key_path"[l])::text, 'null'), '{}')::json,
             "key_path"[l+1:u],
             "value_to_set"
           )
         )
       END
  FROM array_lower("key_path", 1) l,
       array_upper("key_path", 1) u
$function$;

Perbarui : fungsi dipadatkan sekarang.

pozs
sumber
5
Saya mencoba fungsi plpgsql Anda, tetapi saya tidak yakin bagaimana menggunakannya - Saya melihat kesalahan ketika saya mencoba select json_object_set_key((select data from test where data->>'b' = '2'), 'b', 'two'); pesan kesalahan adalah ERROR: could not determine polymorphic type because input has type "unknown"
user9645
1
Ini melakukan yang setara dengan UPSERT, bukan UPDATE. Jika kunci belum ada di bidang json, itu ditambahkan. Lihatlah pertanyaan terkait ini untuk yang sebenarnya UPDATE: stackoverflow.com/questions/7711432/… (Ini untuk tipe komposit, tetapi prinsipnya mirip untuk json.)
Erwin Brandstetter
1
@ ErwinBrandstetter itu benar, tetapi di json, UPSERT biasanya lebih umum daripada modifikasi seperti UPDATE (pertimbangkan f.ex. sqlfiddle.com/#!15/d41d8/2897 ) - Saya menafsirkan pertanyaan asli seperti bagaimana Anda memodifikasi mereka (kolom json) menggunakan pernyataan UPDATE? - Selain itu satu syarat dapat mengubah ini menjadi UPDATE.
pozs
1
Sangat bermanfaat dan lengkap sekarang.
Erwin Brandstetter
1
@maxhud yang tergantung pada klien (atau pustaka klien yang Anda gunakan). Jika Anda bisa, gunakan tipe eksplisit (PostgreSQL dapat menebak tipe dalam query parametrized, tapi itu biasanya tidak berfungsi dengan baik dengan fungsi polimorfik). Tapi setidaknya, Anda bisa menggunakan gips eksplisit, seperti $2::text.
pozs
98

Dengan 9.5 gunakan jsonb_set-

UPDATE objects
SET body = jsonb_set(body, '{name}', '"Mary"', true)
WHERE id = 1; 

di mana tubuh adalah tipe kolom jsonb.

Teo Choong Ping
sumber
Hai, mengapa saya tidak bisa menggunakan upperseperti itu: update objects set body=jsonb_set(body, '{name}', upper('"Mary"'), true) where id=1;itu tidak mengenali, atau bagaimana saya bisa mencapai perilaku yang sama? thx
Rafael Capucho
1
Jika nilai yang ingin saya tetapkan adalah substring dari kolom lain daripada "Mary", bagaimana saya melakukannya?
Andrew
58

Dengan Postgresql 9.5 dapat dilakukan dengan mengikuti-

UPDATE test
SET data = data - 'a' || '{"a":5}'
WHERE data->>'b' = '2';

ATAU

UPDATE test
SET data = jsonb_set(data, '{a}', '5'::jsonb);

Seseorang bertanya bagaimana memperbarui banyak bidang dalam nilai jsonb sekaligus. Misalkan kita membuat tabel:

CREATE TABLE testjsonb ( id SERIAL PRIMARY KEY, object JSONB );

Lalu kami menyisipkan baris eksperimental:

INSERT INTO testjsonb
VALUES (DEFAULT, '{"a":"one", "b":"two", "c":{"c1":"see1","c2":"see2","c3":"see3"}}');

Lalu kami MEMPERBARUI baris:

UPDATE testjsonb SET object = object - 'b' || '{"a":1,"d":4}';

Yang melakukan hal berikut:

  1. Memperbarui bidang
  2. Menghapus bidang b
  3. Tambahkan bidang d

Memilih data:

SELECT jsonb_pretty(object) FROM testjsonb;

Akan menghasilkan:

      jsonb_pretty
-------------------------
 {                      +
     "a": 1,            +
     "c": {             +
         "c1": "see1",  +
         "c2": "see2",  +
         "c3": "see3",  +
     },                 +
     "d": 4             +
 }
(1 row)

Untuk memperbarui bidang di dalam, Jangan gunakan operator khusus ||. Gunakan jsonb_set sebagai gantinya. Yang tidak sederhana:

UPDATE testjsonb SET object =
jsonb_set(jsonb_set(object, '{c,c1}','"seeme"'),'{c,c2}','"seehim"');

Menggunakan operator concat untuk {c, c1} misalnya:

UPDATE testjsonb SET object = object || '{"c":{"c1":"seedoctor"}}';

Akan menghapus {c, c2} dan {c, c3}.

Untuk kekuatan lebih, cari kekuatan di dokumentasi fungsi json postgresql . Seseorang mungkin tertarik pada #-operator, jsonb_setfungsi dan juga jsonb_insertfungsi.

Fandi Susanto
sumber
dan jika saya harus memperbarui dua bidang lalu apa sintaksinya?
Sunil Garg
jika saya memiliki kolom json dengan nama bidang, bagaimana cara menambahkan bidang nama belakang ke kolom ini
Bionix1441
Harus jelas:UPDATE users SET profile = profile || '{"lastname":"Washington"}' WHERE profile->>'name' = 'George Washington';
Fandi Susanto
9

Untuk membangun berdasarkan jawaban @ pozs, berikut adalah beberapa fungsi PostgreSQL lainnya yang mungkin berguna bagi sebagian orang. (Membutuhkan PostgreSQL 9.3+)

Hapus Dengan Kunci: Menghapus nilai dari struktur JSON dengan kunci.

CREATE OR REPLACE FUNCTION "json_object_del_key"(
  "json"          json,
  "key_to_del"    TEXT
)
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT CASE
  WHEN ("json" -> "key_to_del") IS NULL THEN "json"
  ELSE (SELECT concat('{', string_agg(to_json("key") || ':' || "value", ','), '}')
          FROM (SELECT *
                  FROM json_each("json")
                 WHERE "key" <> "key_to_del"
               ) AS "fields")::json
END
$function$;

Hapus Rekursif Dengan Kunci: Menghapus nilai dari struktur JSON oleh jalur-kunci. (membutuhkan json_object_set_keyfungsi @ pozs )

CREATE OR REPLACE FUNCTION "json_object_del_path"(
  "json"          json,
  "key_path"      TEXT[]
)
  RETURNS json
  LANGUAGE sql
  IMMUTABLE
  STRICT
AS $function$
SELECT CASE
  WHEN ("json" -> "key_path"[l] ) IS NULL THEN "json"
  ELSE
     CASE COALESCE(array_length("key_path", 1), 0)
         WHEN 0 THEN "json"
         WHEN 1 THEN "json_object_del_key"("json", "key_path"[l])
         ELSE "json_object_set_key"(
           "json",
           "key_path"[l],
           "json_object_del_path"(
             COALESCE(NULLIF(("json" -> "key_path"[l])::text, 'null'), '{}')::json,
             "key_path"[l+1:u]
           )
         )
       END
    END
  FROM array_lower("key_path", 1) l,
       array_upper("key_path", 1) u
$function$;

Contoh penggunaan:

s1=# SELECT json_object_del_key ('{"hello":[7,3,1],"foo":{"mofu":"fuwa", "moe":"kyun"}}',
                                 'foo'),
            json_object_del_path('{"hello":[7,3,1],"foo":{"mofu":"fuwa", "moe":"kyun"}}',
                                 '{"foo","moe"}');

 json_object_del_key |          json_object_del_path
---------------------+-----------------------------------------
 {"hello":[7,3,1]}   | {"hello":[7,3,1],"foo":{"mofu":"fuwa"}}
shru
sumber
Sangat berguna! Terima kasih.
1111161171159459134
9
UPDATE test
SET data = data::jsonb - 'a' || '{"a":5}'::jsonb
WHERE data->>'b' = '2'

Ini sepertinya bekerja pada PostgreSQL 9.5

sigod
sumber
Bekerja untuk saya, sejauh yang saya mengerti, ini menghapus bidang "a" dari data dan kemudian menambahkan bidang "a" dengan nilai baru. Dalam kasus saya, nilai "a" didasarkan pada kolom. UPDATE test SET data = data :: jsonb - 'a' || ('{"a": "' || myColumn || '"}') :: jsonb;
sebge2
7

Jika jenis bidang Anda adalah json, maka yang berikut ini akan berfungsi untuk Anda.

UPDATE 
table_name
SET field_name = field_name::jsonb - 'key' || '{"key":new_val}' 
WHERE field_name->>'key' = 'old_value'.

Operator '-' menghapus kunci / pasangan nilai atau elemen string dari operan kiri. Pasangan kunci / nilai dicocokkan berdasarkan nilai kunci mereka.

Operator '||' menggabungkan dua nilai jsonb menjadi nilai jsonb baru.

Karena ini adalah operator jsonb, Anda hanya perlu mengetikkan ke :: jsonb

Info lebih lanjut: Fungsi dan Operator JSON

Anda dapat membaca catatan saya di sini

Neethu
sumber
Cara sederhana & lebih baik untuk memperbarui bidang JSON, jika Anda tidak khawatir tentang penyusunan ulang pesanan properti.
Karthik Sivaraj
4

Dengan PostgreSQL 9.4, kami telah mengimplementasikan fungsi python berikut. Mungkin juga bekerja dengan PostgreSQL 9.3.

create language plpython2u;

create or replace function json_set(jdata jsonb, jpaths jsonb, jvalue jsonb) returns jsonb as $$
import json

a = json.loads(jdata)
b = json.loads(jpaths)

if a.__class__.__name__ != 'dict' and a.__class__.__name__ != 'list':
  raise plpy.Error("The json data must be an object or a string.")

if b.__class__.__name__ != 'list':
   raise plpy.Error("The json path must be an array of paths to traverse.")

c = a
for i in range(0, len(b)):
  p = b[i]
  plpy.notice('p == ' + str(p))

  if i == len(b) - 1:
    c[p] = json.loads(jvalue)

  else:
    if p.__class__.__name__ == 'unicode':
      plpy.notice("Traversing '" + p + "'")
      if c.__class__.__name__ != 'dict':
        raise plpy.Error("  The value here is not a dictionary.")
      else:
        c = c[p]

    if p.__class__.__name__ == 'int':
      plpy.notice("Traversing " + str(p))
      if c.__class__.__name__ != 'list':
        raise plpy.Error("  The value here is not a list.")
      else:
        c = c[p]

    if c is None:
      break    

return json.dumps(a)
$$ language plpython2u ;

Contoh penggunaan:

create table jsonb_table (jsonb_column jsonb);
insert into jsonb_table values
('{"cars":["Jaguar", {"type":"Unknown","partsList":[12, 34, 56]}, "Atom"]}');

select jsonb_column->'cars'->1->'partsList'->2, jsonb_column from jsonb_table;

update jsonb_table
set jsonb_column = json_set(jsonb_column, '["cars",1,"partsList",2]', '99');

select jsonb_column->'cars'->1->'partsList'->2, jsonb_column from jsonb_table;

Perhatikan bahwa untuk majikan sebelumnya, saya telah menulis satu set fungsi C untuk memanipulasi data JSON sebagai teks (bukan sebagai jsonatau jsonbjenis) untuk PostgreSQL 7, 8 dan 9. Misalnya, mengekstraksi data dengan json_path('{"obj":[12, 34, {"num":-45.67}]}', '$.obj[2]['num']'), mengatur data dengan json_path_set('{"obj":[12, 34, {"num":-45.67}]}', '$.obj[2]['num']', '99.87')dan sebagainya. Butuh waktu sekitar 3 hari kerja, jadi jika Anda perlu menjalankannya pada sistem lama dan punya waktu luang, mungkin sepadan dengan usaha. Saya membayangkan versi C jauh lebih cepat daripada versi python.

Magnus
sumber
2

Meskipun yang berikut tidak akan memenuhi permintaan ini (fungsi json_object_agg tidak tersedia di PostgreSQL 9.3), yang berikut ini dapat bermanfaat bagi siapa saja yang mencari || operator untuk PostgreSQL 9.4, sebagaimana diterapkan dalam PostgreSQL 9.5 mendatang:

CREATE OR REPLACE FUNCTION jsonb_merge(left JSONB, right JSONB)
RETURNS JSONB
AS $$
SELECT
  CASE WHEN jsonb_typeof($1) = 'object' AND jsonb_typeof($2) = 'object' THEN
       (SELECT json_object_agg(COALESCE(o.key, n.key), CASE WHEN n.key IS NOT NULL THEN n.value ELSE o.value END)::jsonb
        FROM jsonb_each($1) o
        FULL JOIN jsonb_each($2) n ON (n.key = o.key))
   ELSE 
     (CASE WHEN jsonb_typeof($1) = 'array' THEN LEFT($1::text, -1) ELSE '['||$1::text END ||', '||
      CASE WHEN jsonb_typeof($2) = 'array' THEN RIGHT($2::text, -1) ELSE $2::text||']' END)::jsonb
   END     
$$ LANGUAGE sql IMMUTABLE STRICT;
GRANT EXECUTE ON FUNCTION jsonb_merge(jsonb, jsonb) TO public;
CREATE OPERATOR || ( LEFTARG = jsonb, RIGHTARG = jsonb, PROCEDURE = jsonb_merge );
Ziggy Crueltyfree Zeitgeister
sumber
2

Saya menulis fungsi kecil untuk diri saya sendiri yang bekerja secara rekursif di Postgres 9.4. Inilah fungsinya (saya harap ini berfungsi dengan baik untuk Anda):

CREATE OR REPLACE FUNCTION jsonb_update(val1 JSONB,val2 JSONB)
RETURNS JSONB AS $$
DECLARE
    result JSONB;
    v RECORD;
BEGIN
    IF jsonb_typeof(val2) = 'null'
    THEN 
        RETURN val1;
    END IF;

    result = val1;

    FOR v IN SELECT key, value FROM jsonb_each(val2) LOOP

        IF jsonb_typeof(val2->v.key) = 'object'
            THEN
                result = result || jsonb_build_object(v.key, jsonb_update(val1->v.key, val2->v.key));
            ELSE
                result = result || jsonb_build_object(v.key, v.value);
        END IF;
    END LOOP;

    RETURN result;
END;
$$ LANGUAGE plpgsql;

Berikut ini contoh penggunaannya:

select jsonb_update('{"a":{"b":{"c":{"d":5,"dd":6},"cc":1}},"aaa":5}'::jsonb, '{"a":{"b":{"c":{"d":15}}},"aa":9}'::jsonb);
                            jsonb_update                             
---------------------------------------------------------------------
 {"a": {"b": {"c": {"d": 15, "dd": 6}, "cc": 1}}, "aa": 9, "aaa": 5}
(1 row)

Seperti yang Anda lihat itu menganalisis dalam-dalam dan memperbarui / menambah nilai jika diperlukan.

J. Raczkiewicz
sumber
2

Ini berfungsi untuk saya, ketika mencoba memperbarui bidang tipe string.

UPDATE table_name 
SET body = jsonb_set(body, '{some_key}', to_json('value'::TEXT)::jsonb);

Semoga ini bisa membantu orang lain!

Mengasumsikan table table_name memiliki kolom jsonb bernama body dan Anda ingin mengubah body.some_key = 'value'

Antonio
sumber
sayangnya ini memformat ulang JSON dengan cara yang sama seperti manipulasi melalui fungsi spesifik JSON
Lu55
1

Sayangnya, saya belum menemukan apa pun di dokumentasi, tetapi Anda dapat menggunakan beberapa solusi, misalnya Anda dapat menulis beberapa fungsi tambahan.

Misalnya, dalam Python:

CREATE or REPLACE FUNCTION json_update(data json, key text, value json)
returns json
as $$
from json import loads, dumps
if key is None: return data
js = loads(data)
js[key] = value
return dumps(js)
$$ language plpython3u

lalu

update test set data=json_update(data, 'a', to_json(5)) where data->>'b' = '2';
Roman Pekar
sumber
Sayang sekali Amazon RDS tidak mendukung plpython3u!
dbau
2
Ini valuejuga akan memerlukan loadsketika mengatur nilai-nilai non numerik seperti string ( js[key] = loads(value)) - Jika tidak:select json_update('{"a":"a"}', 'a', to_json('b')); -> {"a": "\"b\""}
hooblei
Jawaban ini juga dapat dimodifikasi untuk memasukkan penghapusan kunci ketika nilai diatur ke Tidak ada: `jika nilai tidak ada: data del [kunci]
Joshua Burns
1

Cuplikan plpython berikut mungkin berguna.

CREATE EXTENSION IF NOT EXISTS plpythonu;
CREATE LANGUAGE plpythonu;

CREATE OR REPLACE FUNCTION json_update(data json, key text, value text)
 RETURNS json
 AS $$
    import json
    json_data = json.loads(data)
    json_data[key] = value
    return json.dumps(json_data, indent=4)
 $$ LANGUAGE plpythonu;

-- Check how JSON looks before updating

SELECT json_update(content::json, 'CFRDiagnosis.mod_nbs', '1')
FROM sc_server_centre_document WHERE record_id = 35 AND template = 'CFRDiagnosis';

-- Once satisfied update JSON inplace

UPDATE sc_server_centre_document SET content = json_update(content::json, 'CFRDiagnosis.mod_nbs', '1')
WHERE record_id = 35 AND template = 'CFRDiagnosis';
Sandeep
sumber
1

Saya menemukan jawaban sebelumnya adalah pengguna PostgreSQL berpengalaman yang cocok, maka jawaban saya:

Asumsikan Anda memiliki kolom-tabel tipe JSONB dengan nilai berikut:

{
    "key0": {
        "key01": "2018-05-06T12:36:11.916761+00:00",
        "key02": "DEFAULT_WEB_CONFIGURATION",

    "key1": {
        "key11": "Data System",
        "key12": "<p>Health,<p>my address<p>USA",
        "key13": "*Please refer to main screen labeling"
    }
}

mari kita asumsikan kita ingin menetapkan nilai baru di baris:

"key13": "*Please refer to main screen labeling"

dan sebagai gantinya letakkan nilainya:

"key13": "See main screen labeling"

kita menggunakan fungsi json_set () untuk menetapkan nilai baru ke key13

parameter ke jsonb_set ()

jsonb_set(target jsonb, path text[], new_value jsonb[, create_missing boolean])

di " target " - Saya akan menempatkan nama kolom jsonb (ini adalah kolom tabel yang sedang dimodifikasi)

" path " - adalah "path json keys" yang mengarah ke (dan termasuk) kunci yang akan kita timpa

" new_value " - ini adalah nilai baru yang kami berikan

dalam kasus kami, kami ingin memperbarui nilai key13 yang berada di bawah key1 (key1 -> key13):

maka sintaks pathnya adalah: '{key1, key13}' (Path adalah bagian yang paling sulit untuk diretas - karena tutorialnya sangat buruk)

jsonb_set(jsonb_column,'{key1,key13}','"See main screen labeling"')
Dror
sumber
0

Anda juga dapat menambahkan kunci secara atomis dalam jsonbseperti ini:

UPDATE users SET counters = counters || CONCAT('{"bar":', COALESCE(counters->>'bar','0')::int + 1, '}')::jsonb WHERE id = 1;

SELECT * FROM users;

 id |    counters
----+------------
  1 | {"bar": 1}

Kunci tidak terdefinisi -> mengasumsikan nilai awal 0.

Untuk penjelasan lebih rinci, lihat jawaban saya di sini: https://stackoverflow.com/a/39076637

joonas.fi
sumber
0

Bagi yang menggunakan mybatis, berikut adalah contoh pernyataan pembaruan:

<update id="saveAnswer">
    update quiz_execution set answer_data = jsonb_set(answer_data, concat('{', #{qid}, '}')::text[], #{value}::jsonb), updated_at = #{updatedAt}
    where id = #{id}
</update>


Params:

  • qid, kunci untuk bidang.
  • value, adalah string json yang valid, untuk nilai bidang,
    misalnya dikonversi dari objek ke string json via jackson,
Eric Wang
sumber
0

Jadi, misalnya string saya terlihat seperti ini: {"a1": {"a11": "x", "a22": "y", "a33": "z"}}

Saya memperbarui jsons dengan menggunakan tabel temp, yang cukup bagus untuk jumlah data yang sedikit (<1.000.000). Saya menemukan cara yang berbeda, tetapi kemudian pergi berlibur dan melupakannya ...

Begitu. kueri akan menjadi seperti ini:

with temp_table as (
select 
a.id,
a->'a1'->>'a11' as 'a11',
a->'a1'->>'a22' as 'a22',
a->'a1'->>'a33' as 'a33',
u1.a11updated
from foo a
join table_with_updates u1 on u1.id = a.id)
    update foo a
    set a = ('{"a1": {"a11": "'|| t.a11updated ||'",
        "a22":"'|| t.a22 ||'",
        "a33":"'|| t.a33 ||'"}}')::jsonb
    from temp_table t
    where t.id = a.id;

Ini lebih banyak berkaitan dengan string daripada json, tetapi ia bekerja. Pada dasarnya, ini menarik semua data ke tabel temp, membuat string sambil mencolokkan lubang-lubang concat dengan data yang Anda buat cadangannya, dan mengubahnya menjadi jsonb.

Json_set mungkin lebih efisien, tapi saya masih bisa menguasainya. Pertama kali saya mencoba menggunakannya, saya mengacaukan string sepenuhnya ...

Vlad S
sumber
1
hai dan selamat datang di StackOverflow! Perhatikan bahwa sudah ada jawaban yang diterima untuk pertanyaan ini.
hongsy
-2

Jika Anda membuat kueri ini dengan klien bahasa pemrograman, misalnya dari python pycopg2, atau Node Postgres, Pastikan Anda mengurai data baru ke JSON terlebih dahulu.

Mungkin dengan mudah terlihat seperti kamus python adalah Sama seperti objek JSON tetapi tidak terlebih dahulu melakukan json.dumps pada kamus.

Cuplikan python sederhana:

def change_destination(self,parcel_id,destlatlng): query="UPDATE parcels SET destlatlng = '{}' WHERE parcel_id ={};".format(json.dumps(destlatlng), parcel_id) self.cursor.execute(query2) self.connection.commit()

Cryce Truly
sumber