Làm cách nào để sửa đổi các trường bên trong kiểu dữ liệu JSON PostgreQuery mới?


235

Với postgresql 9.3 tôi có thể CHỌN các trường cụ thể của loại dữ liệu JSON, nhưng làm thế nào để bạn sửa đổi chúng bằng cách sử dụng CẬP NHẬT? Tôi không thể tìm thấy bất kỳ ví dụ nào về điều này trong tài liệu postgresql hoặc bất cứ nơi nào trực tuyến. Tôi đã thử một điều hiển nhiên:

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...

Câu trả lời:


330

Cập nhật : Với PostgreQuery 9.5 , có một số jsonbchức năng thao tác trong chính PostgreSQL (nhưng không có chức năng nào jsonđược yêu cầu để thao tác jsoncác giá trị).

Hợp nhất 2 (hoặc nhiều hơn) các đối tượng JSON (hoặc nối các mảng):

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]'

Vì vậy, thiết lập một phím đơn giản có thể được thực hiện bằng cách sử dụng:

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

Trường hợp <key>nên là chuỗi, và <value>có thể là bất cứ loại nào to_jsonb()chấp nhận.

Để đặt giá trị sâu trong cấu trúc phân cấp JSON , jsonb_set()có thể sử dụng hàm:

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

Danh sách tham số đầy đủ của jsonb_set():

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

pathcũng có thể chứa các chỉ mục mảng JSON và các số nguyên âm xuất hiện ở đó được tính từ cuối các mảng JSON. Tuy nhiên, một chỉ mục mảng JSON không tồn tại nhưng tích cực sẽ nối phần tử vào cuối mảng:

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

Để chèn vào mảng JSON (trong khi bảo toàn tất cả các giá trị ban đầu) , jsonb_insert()có thể sử dụng hàm ( trong 9.6+; chỉ chức năng này, trong phần này ):

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]}]}'

Danh sách tham số đầy đủ của jsonb_insert():

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

Một lần nữa, các số nguyên âm xuất hiện pathđếm từ cuối mảng JSON.

Vì vậy, f.ex. nối thêm vào một mảng JSON có thể được thực hiện với:

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

Tuy nhiên, hàm này hoạt động hơi khác (so với jsonb_set()) khi khóa pathtrong targetlà một đối tượng JSON. Trong trường hợp đó, nó sẽ chỉ thêm một cặp khóa-giá trị mới cho đối tượng JSON khi khóa không được sử dụng. Nếu nó được sử dụng, nó sẽ phát sinh lỗi:

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

Xóa một khóa (hoặc một chỉ mục) khỏi một đối tượng JSON (hoặc, từ một mảng) có thể được thực hiện với -toán tử:

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

Việc xóa, từ sâu trong hệ thống phân cấp JSON có thể được thực hiện với #-toán tử:

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

Trong phiên bản 9.4 , bạn có thể sử dụng phiên bản sửa đổi của câu trả lời gốc (bên dưới), nhưng thay vì tổng hợp chuỗi JSON, bạn có thể tổng hợp thành một đối tượng json trực tiếp json_object_agg().

Câu trả lời gốc : Cũng có thể (không có plpython hoặc plv8) trong SQL thuần túy (nhưng cần 9.3+, sẽ không hoạt động với 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

Chỉnh sửa :

Một phiên bản, đặt nhiều khóa & giá trị:

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$;

Chỉnh sửa 2 : như @ErwinBrandstetter lưu ý các chức năng trên ở trên hoạt động giống như cái gọi là UPSERT(cập nhật một trường nếu nó tồn tại, chèn nếu nó không tồn tại). Đây là một biến thể, chỉ 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$;

Chỉnh sửa 3 : Đây là biến thể đệ quy, có thể đặt ( UPSERT) giá trị lá (và sử dụng hàm đầu tiên từ câu trả lời này), nằm ở đường dẫn khóa (trong đó các khóa chỉ có thể tham chiếu đến các đối tượng bên trong, mảng bên trong không được hỗ trợ):

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$;

Cập nhật : các chức năng được nén ngay bây giờ.


5
Tôi đã thử chức năng plpgsql của bạn, nhưng tôi không chắc cách sử dụng nó - Tôi gặp lỗi khi tôi thử select json_object_set_key((select data from test where data->>'b' = '2'), 'b', 'two'); thông báo lỗi là ERROR: could not determine polymorphic type because input has type "unknown"
user9645

1
Điều này thực hiện tương đương với một UPSERT, không phải một UPDATE. Nếu khóa chưa tồn tại trong trường json, nó sẽ được thêm vào. Hãy xem câu hỏi liên quan này để biết thực tế UPDATE: stackoverflow.com/questions/7711432/ cấp (Đây là loại tổng hợp, nhưng hiệu trưởng tương tự với json.)
Erwin Brandstetter

1
@ErwinBrandstetter là đúng, nhưng trong json, một UPSERT thường chung chung hơn một sửa đổi giống như CẬP NHẬT (xem xét f.ex. sqlfiddle.com/#!15/d41d8/2897 ) - Tôi diễn giải câu hỏi ban đầu là cách bạn sửa đổi chúng (cột json) bằng cách sử dụng câu lệnh CẬP NHẬT? - bên cạnh một điều kiện duy nhất có thể chuyển đổi điều này thành CẬP NHẬT.
pozs

1
Rất hữu ích và hoàn thành ngay bây giờ.
Erwin Brandstetter

1
@maxhud phụ thuộc vào khách hàng (hoặc thư viện khách bạn sử dụng). Nếu bạn có thể, hãy sử dụng các loại rõ ràng (PostgreSQL có thể đoán các loại trong các truy vấn được tham số hóa, nhưng điều đó thường không hoạt động tốt với các hàm đa hình). Nhưng ít nhất, bạn có thể sử dụng phôi rõ ràng, như $2::text.
pozs

98

Với 9,5 sử dụng jsonb_set-

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

trong đó cơ thể là một loại cột jsonb.


Xin chào, tại sao tôi không thể sử dụng uppernhư vậy: update objects set body=jsonb_set(body, '{name}', upper('"Mary"'), true) where id=1;nó không nhận ra hoặc làm thế nào tôi có thể đạt được hành vi tương tự? thx
Rafael Capucho

1
Nếu giá trị tôi muốn đặt là một chuỗi con từ một cột khác thay vì "Mary", tôi sẽ làm thế nào?
Andrew

58

Với Postgresql 9.5, nó có thể được thực hiện bằng cách làm theo-

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

HOẶC LÀ

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

Ai đó hỏi làm thế nào để cập nhật nhiều trường trong giá trị jsonb cùng một lúc. Giả sử chúng ta tạo một bảng:

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

Sau đó, chúng tôi CHỌN một hàng thử nghiệm:

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

Sau đó, chúng tôi CẬP NHẬT hàng:

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

Mà làm như sau:

  1. Cập nhật một lĩnh vực
  2. Xóa trường b
  3. Thêm trường d

Chọn dữ liệu:

SELECT jsonb_pretty(object) FROM testjsonb;

Sẽ cho kết quả:

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

Để cập nhật trường bên trong, Không sử dụng toán tử concat ||. Sử dụng jsonb_set thay thế. Điều này không đơn giản:

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

Sử dụng toán tử concat cho {c, c1} chẳng hạn:

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

Sẽ xóa {c, c2} và {c, c3}.

Để có thêm sức mạnh, hãy tìm kiếm sức mạnh tại tài liệu hàm json postgresql . Người ta có thể quan tâm đến #-toán tử, jsonb_setchức năng và jsonb_insertchức năng.


và nếu tôi phải cập nhật hai trường thì cú pháp là gì?
Sunil Garg

nếu tôi có một cột json với tên trường, làm cách nào để thêm trường họ vào cột này
Bionix1441

Cần phải rõ ràng:UPDATE users SET profile = profile || '{"lastname":"Washington"}' WHERE profile->>'name' = 'George Washington';
Fandi Susanto

9

Để xây dựng dựa trên câu trả lời của @ pozs, đây là một vài hàm PostgreQuery khác có thể hữu ích với một số người. (Yêu cầu PostgreSQL 9.3+)

Xóa theo khóa: Xóa một giá trị khỏi cấu trúc JSON bằng khóa.

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$;

Xóa đệ quy theo khóa: Xóa một giá trị khỏi cấu trúc JSON theo đường dẫn khóa. (yêu cầu json_object_set_keychức năng của @ 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$;

Ví dụ sử dụng:

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"}}

Rất hữu ích! Cảm ơn bạn.
1111161171159459134

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

Điều này dường như đang hoạt động trên PostgreQuery 9.5


Làm việc cho tôi, theo như tôi đã hiểu, điều này loại bỏ trường "a" khỏi dữ liệu và sau đó nối thêm trường "a" với giá trị mới. Trong trường hợp của tôi, giá trị của "a" được dựa trên một cột. CẬP NHẬT kiểm tra dữ liệu SET = data :: jsonb - 'a' || ('{"a": "' | | myColumn || '"}') :: jsonb;
sebge2

7

Nếu loại trường của bạn là json, sau đây sẽ làm việc cho bạn.

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

Toán tử '-' xóa cặp khóa / giá trị hoặc phần tử chuỗi khỏi toán hạng bên trái. Các cặp khóa / giá trị được khớp dựa trên giá trị khóa của chúng.

Toán tử '||' nối hai giá trị jsonb thành một giá trị jsonb mới.

Vì đây là các toán tử jsonb, bạn chỉ cần gõ typecast thành :: jsonb

Thông tin thêm: Hàm và toán tử JSON

Bạn có thể đọc ghi chú của tôi ở đây


Cách đơn giản và tốt hơn để cập nhật các trường JSON, nếu bạn không lo lắng về việc sắp xếp lại thứ tự thuộc tính.
Karthik Sivaraj

4

Với PostgreQuery 9.4, chúng tôi đã triển khai chức năng python sau đây. Nó cũng có thể hoạt động với 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 ;

Ví dụ sử dụng:

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;

Lưu ý rằng đối với một chủ nhân trước đây, tôi đã viết một tập hợp các hàm C để thao tác dữ liệu JSON dưới dạng văn bản (không phải là một jsonhoặc jsonbloại) cho PostgreQuery 7, 8 và 9. Ví dụ: trích xuất dữ liệu json_path('{"obj":[12, 34, {"num":-45.67}]}', '$.obj[2]['num']'), cài đặt dữ liệu json_path_set('{"obj":[12, 34, {"num":-45.67}]}', '$.obj[2]['num']', '99.87'), v.v. Mất khoảng 3 ngày làm việc, vì vậy nếu bạn cần nó để chạy trên các hệ thống cũ và có thời gian rảnh rỗi, nó có thể là nỗ lực đáng giá. Tôi tưởng tượng phiên bản C nhanh hơn nhiều so với phiên bản python.


2

Mặc dù những điều sau đây sẽ không thỏa mãn yêu cầu này (hàm json_object_agg không có sẵn trong PostgreQuery 9.3), những điều sau đây có thể hữu ích cho bất kỳ ai đang tìm kiếm | | toán tử cho PostgreQuery 9.4, như được triển khai trong PostgreQuery 9.5 sắp tới:

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 );

2

Tôi đã viết hàm nhỏ cho chính mình mà hoạt động đệ quy trong Postgres 9.4. Đây là chức năng (tôi hy vọng nó hoạt động tốt cho bạn):

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;

Đây là mẫu sử dụng:

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)

Như bạn có thể thấy nó phân tích sâu và cập nhật / thêm giá trị khi cần thiết.


2

Điều này làm việc cho tôi, khi cố gắng cập nhật trường loại chuỗi.

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

Hy vọng nó sẽ giúp người khác ra ngoài!

Giả sử bảng tên_bảng có một cột jsonb có tên là body và bạn muốn thay đổi body.some_key = 'value'


Thật không may, điều này định dạng lại JSON giống như các thao tác thông qua các hàm dành riêng cho JSON
Lu55 27/03/18

1

Đáng buồn thay, tôi đã không tìm thấy bất cứ điều gì trong tài liệu, nhưng bạn có thể sử dụng một số cách giải quyết, ví dụ bạn có thể viết một số chức năng mở rộng.

Ví dụ: trong 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

và sau đó

update test set data=json_update(data, 'a', to_json(5)) where data->>'b' = '2';

Thật đáng tiếc Amazon RDS không hỗ trợ plpython3u!
dbau

2
Điều valuenày cũng sẽ yêu cầu loadskhi thiết lập các giá trị không phải là số như chuỗi ( js[key] = loads(value))select json_update('{"a":"a"}', 'a', to_json('b')); -> {"a": "\"b\""}
Mặt

Câu trả lời này cũng có thể được sửa đổi để bao gồm xóa khóa khi giá trị được đặt thành Không: `nếu giá trị là Không có: del data [key]
Joshua Burns

1

Đoạn mã plpython sau đây có thể có ích.

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';

1

Tôi tìm thấy câu trả lời trước là người dùng PostgreQuery có kinh nghiệm phù hợp, do đó câu trả lời của tôi:

Giả sử bạn có một cột bảng loại JSONB với giá trị sau:

{
    "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"
    }
}

giả sử chúng ta muốn đặt một giá trị mới trong hàng:

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

và thay vào đó đặt giá trị:

"key13": "See main screen labeling"

chúng tôi sử dụng hàm json_set () để gán giá trị mới cho key13

các tham số cho jsonb_set ()

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

trong " mục tiêu " - Tôi sẽ đặt tên cột jsonb (đây là cột bảng đang được sửa đổi)

" đường dẫn " - là "đường dẫn khóa json" dẫn đến (và bao gồm) khóa mà chúng ta sẽ ghi đè

" new_value " - đây là giá trị mới mà chúng tôi gán

trong trường hợp của chúng tôi, chúng tôi muốn cập nhật giá trị của key13 nằm trong key1 (key1 -> key13):

do đó cú pháp đường dẫn là: '{key1, key13}' (Đường dẫn là phần khó nhất để bẻ khóa - bởi vì các hướng dẫn rất tệ)

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

0

Bạn cũng có thể tăng các khóa nguyên tử trong phạm vi jsonbnhư thế này:

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}

Khóa không xác định -> giả sử giá trị bắt đầu là 0.

Để được giải thích chi tiết hơn, xem câu trả lời của tôi ở đây: https://stackoverflow.com/a/39076637


0

Đối với những người sử dụng mybatis, đây là một tuyên bố cập nhật ví dụ:

<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>


Param:

  • qid, chìa khóa cho lĩnh vực.
  • value, là một chuỗi json hợp lệ, cho giá trị trường,
    ví dụ: được chuyển đổi từ đối tượng sang chuỗi json thông qua jackson,

0

Vì vậy, ví dụ chuỗi của tôi trông như thế này: {"a1": {"a11": "x", "a22": "y", "a33": "z"}}

Tôi cập nhật jsons bằng cách sử dụng bảng tạm thời, đủ tốt cho lượng dữ liệu khá nhỏ (<1.000.000). Tôi tìm thấy một cách khác, nhưng sau đó đi nghỉ và quên nó ...

Vì thế. truy vấn sẽ giống như thế này:

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;

Nó có nhiều thứ để làm với chuỗi hơn json, nhưng nó hoạt động. Về cơ bản, nó kéo tất cả dữ liệu vào bảng tạm thời, tạo một chuỗi trong khi cắm các lỗ concat với dữ liệu bạn đã sao lưu và chuyển đổi nó thành jsonb.

Json_set có thể hiệu quả hơn, nhưng tôi vẫn đang hiểu rõ về nó. Lần đầu tiên tôi thử sử dụng nó, tôi đã làm rối hoàn toàn chuỗi ...


1
xin chào và chào mừng đến với StackOverflow! Lưu ý rằng đã có một câu trả lời được chấp nhận cho câu hỏi này.
hongsy

-2

Nếu bạn đang thực hiện truy vấn này với ứng dụng khách ngôn ngữ lập trình, ví dụ: từ python pycopg2, hoặc Node Postgres, trước tiên hãy đảm bảo bạn phân tích dữ liệu mới sang JSON.

Nó có thể dễ dàng trông giống như một từ điển python giống như một đối tượng JSON nhưng trước tiên nó không làm json.dumps trên từ điển.

Một đoạn trăn đơn giản:

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()

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.