315

postgresql 9.3 ではSELECT、JSON データ型のフィールドを特定できますが、どのように変更しますUPDATEか? この例は、postgresql のドキュメントやオンラインのどこにも見つかりません。私は明白なことを試しました:

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

21 に答える 21

423

更新: PostgreSQL 9.5では、PostgreSQL 自体にいくつかのjsonb操作機能があります (ただし、値jsonを操作するために ; キャストは必要ありませんjson)。

2 つ (またはそれ以上) の JSON オブジェクトをマージする (または配列を連結する):

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

したがって、単純なキーの設定は次を使用して行うことができます。

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

文字<key>列である必要があり、受け入れ<value>られる任意の型にすることができます。to_jsonb()

JSON 階層の深い値を設定するには、次のjsonb_set()関数を使用できます。

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

の完全なパラメータ リストjsonb_set():

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

pathJSON配列インデックスも含めることができ、そこに表示される負の整数はJSON配列の末尾から数えます。ただし、存在しないが正の JSON 配列インデックスは、要素を配列の末尾に追加します。

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

JSON 配列に挿入する場合 (元の値をすべて保持しながら)、関数jsonb_insert()を使用できます ( 9.6 以降; このセクションではこの関数のみ):

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

の完全なパラメータ リストjsonb_insert():

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

pathここでも、 JSON 配列の末尾から数える負の整数が表示されます。

だから、f.ex。JSON 配列の末尾への追加は、次の方法で実行できます。

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

ただし、 inが JSON オブジェクトのキーであるjsonb_set()場合、この関数の動作は ( とは)少し異なります。その場合、キーが使用されていない場合にのみ、JSON オブジェクトの新しいキーと値のペアが追加されます。使用すると、エラーが発生します。pathtarget

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

JSON オブジェクト (または配列) からキー (またはインデックス) を削除する-には、次の演算子を使用します。

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

JSON 階層の深いところから削除するには、次の#-演算子を使用します。

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

9.4の場合、元の回答 (以下) の修正版を使用できますが、JSON 文字列を集約する代わりに、 を使用して json オブジェクトに直接集約できますjson_object_agg()

元の回答:純粋なSQLでも(plpythonまたはplv8なしで)可能です(ただし、9.3以降が必要です。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$;

SQLフィドル

編集

複数のキーと値を設定するバージョン:

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

編集2:@ErwinBrandstetterが指摘したように、上記のこれらの関数はいわゆるUPSERT(存在する場合はフィールドを更新し、存在しない場合は挿入します)。これはバリアントです。これは次の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$;

編集3UPSERT :これは、キーパス(キーは内部オブジェクトのみを参照でき、内部配列はサポートされていません)にあるリーフ値を設定(およびこの回答の最初の関数を使用)できる再帰バリアントです:

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

更新: 既存の json フィールドのキーを別の特定のキーに置き換える機能を追加しました。移行またはデータ構造の修正などの他のシナリオでデータ型を更新する場合に便利です。

CREATE OR REPLACE FUNCTION json_object_replace_key(
    json_value json,
    existing_key text,
    desired_key text)
  RETURNS json AS
$BODY$
SELECT COALESCE(
(
    SELECT ('{' || string_agg(to_json(key) || ':' || value, ',') || '}')
    FROM (
        SELECT *
        FROM json_each(json_value)
        WHERE key <> existing_key
        UNION ALL
        SELECT desired_key, json_value -> existing_key
    ) AS "fields"
    -- WHERE value IS NOT NULL (Actually not required as the string_agg with value's being null will "discard" that entry)

),
    '{}'
)::json
$BODY$
  LANGUAGE sql IMMUTABLE STRICT
  COST 100;

更新: 関数は現在圧縮されています。

于 2014-05-06T17:00:31.710 に答える
148

9.5 では jsonb_set- を使用します。

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

ここで、body は jsonb 列タイプです。

于 2016-01-21T06:09:41.053 に答える
90

Postgresql 9.5 では、次の方法で実行できます。

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

また

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

jsonb 値の多くのフィールドを一度に更新する方法を誰かが尋ねました。テーブルを作成するとします。

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

次に、実験的な行を挿入します。

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

次に、行を更新します。

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

次のことを行います。

  1. a フィールドを更新します
  2. b フィールドを削除します
  3. d フィールドを追加する

データの選択:

SELECT jsonb_pretty(object) FROM testjsonb;

結果は次のとおりです。

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

内部のフィールドを更新するには、concat operator を使用しないで||ください。代わりに jsonb_set を使用してください。これは単純ではありません:

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

たとえば、{c,c1} に concat 演算子を使用すると、次のようになります。

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

{c,c2} と {c,c3} を削除します。

より強力な機能については、 postgresql json functions documentationで機能を探してください。#-演算子、jsonb_set関数、および関数に興味があるかもしれませんjsonb_insert

于 2016-06-27T04:13:02.537 に答える
15

フィールド タイプが json の場合、次のように動作します。

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

演算子 '-' は、左側のオペランドからキーと値のペアまたは文字列要素を削除します。キーと値のペアは、キー値に基づいて照合されます。

演算子 '||' 2 つの jsonb 値を新しい jsonb 値に連結します。

これらは jsonb 演算子なので、::jsonb に型キャストするだけです。

詳細: JSON 関数と演算子

ここで私のメモを読むことができます

于 2018-05-03T09:12:48.730 に答える
12
UPDATE test
SET data = data::jsonb - 'a' || '{"a":5}'::jsonb
WHERE data->>'b' = '2'

これはPostgreSQL 9.5で動作しているようです

于 2016-07-07T11:47:49.933 に答える
9

@pozsの回答に基づいて構築するために、いくつかの便利なPostgreSQL関数をいくつか紹介します。(PostgreSQL 9.3 以降が必要)

キーで削除: JSON 構造からキーで値を削除します。

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

Recursive Delete By Key: キーパスによって JSON 構造から値を削除します。(@pozs のjson_object_set_key関数が必要です)

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

使用例:

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"}}
于 2015-05-14T18:36:16.120 に答える
4

PostgreSQL 9.4 では、次の python 関数を実装しました。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 ;

使用例:

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;

以前の雇用者のために、PostgreSQL 7、8、および 9 のJSON データをテキストとして (jsonまたは型ではなく) 操作するための一連の C 関数を作成したことに注意してください。たとえば、 を使用してデータを抽出し、 を使用してデータを設定します。約 3 日間の作業が必要だったので、レガシー システムで実行する必要があり、時間に余裕がある場合は、努力する価値があるかもしれません。CバージョンはPythonバージョンよりもはるかに高速だと思います。jsonbjson_path('{"obj":[12, 34, {"num":-45.67}]}', '$.obj[2]['num']')json_path_set('{"obj":[12, 34, {"num":-45.67}]}', '$.obj[2]['num']', '99.87')

于 2015-02-24T00:23:28.687 に答える
2

jsonb次のように、キーをアトミックにインクリメントすることもできます。

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}

未定義のキー -> 開始値を 0 と見なします。

詳細な説明については、こちらの回答を参照してください: https://stackoverflow.com/a/39076637

于 2016-08-22T10:07:57.307 に答える
2

Postgres 9.4 で再帰的に動作する小さな関数を自分用に作成しました。これが機能です(うまくいくことを願っています):

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;

使用例は次のとおりです。

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)

ご覧のとおり、深く分析し、必要に応じて値を更新/追加します。

于 2016-10-13T19:48:21.080 に答える
2

以下はこの要求を満たしていませんが (関数 json_object_agg は PostgreSQL 9.3 では使用できません)、|| を探している人には次のものが役立ちます。次のPostgreSQL 9.5で実装されるように、PostgreSQL 9.4の演算子:

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 );
于 2015-11-25T05:23:50.860 に答える
1

このソリューションについてどう思いますか?

新しい値を追加するか、既存の値を更新します。

編集:nullおよび空のオブジェクトで機能するように編集

Edit2:オブジェクト内のオブジェクトで動作するように編集...

create or replace function updateJsonb(object1 json, object2 json)
returns jsonb
language plpgsql
as
$$
declare
    result jsonb;
    tempObj1 text;
    tempObj2 text;

begin
    tempObj1 = substr(object1::text, 2, length(object1::text) - 2); --remove the first { and last }
    tempObj2 = substr(object2::text, 2, length(object2::text) - 2); --remove the first { and last }

    IF object1::text != '{}' and object1::text != 'null' and object1::text != '[]' THEN
        result = ('{' || tempObj1 || ',' || tempObj2 || '}')::jsonb;
    ELSE
        result = ('{' || tempObj2 || '}')::jsonb;
    END IF;
    return result;
end;
$$;

利用方法:

update table_name
set data = updatejsonb(data, '{"test": "ok"}'::json)
于 2020-08-13T09:27:26.473 に答える