[json] 새로운 PostgreSQL JSON 데이터 유형 내에서 필드를 어떻게 수정합니까?
postgresql 9.3에서 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...
답변
업데이트 : PostgreSQL 9.5 에는 jsonb
PostgreSQL 자체에 일부 조작 기능이 있습니다 (그러나 값 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)
path
JSON 배열 색인을 포함 할 수 있으며 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
그러나,이 기능은 약간 다르게 (이상 작동 jsonb_set()
)이 경우 path
에는 target
JSON 개체의 핵심입니다. 이 경우 키를 사용하지 않을 때 JSON 객체에 대한 새 키-값 쌍만 추가합니다. 사용하면 오류가 발생합니다.
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$;
편집 :
여러 키와 값을 설정하는 버전 :
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$;
편집 3 : 여기 UPSERT
에는 키 경로 (키는 내부 객체 만 참조 할 수 있으며 내부 배열은 지원되지 않음)에 위치한 리프 값을 설정 하고이 답변의 첫 번째 함수를 사용하는 재귀 변형 입니다.
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$;
업데이트 : 기능이 압축되었습니다.
답변
9.5에서는 jsonb_set-
UPDATE objects
SET body = jsonb_set(body, '{name}', '"Mary"', true)
WHERE id = 1;
여기서 body는 jsonb 열 유형입니다.
답변
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}';
다음은 무엇입니까?
- 필드를 업데이트합니다
- b 필드를 제거합니다
- d 필드 추가
데이터 선택 :
SELECT jsonb_pretty(object) FROM testjsonb;
결과는 다음과 같습니다.
jsonb_pretty
-------------------------
{ +
"a": 1, +
"c": { +
"c1": "see1", +
"c2": "see2", +
"c3": "see3", +
}, +
"d": 4 +
}
(1 row)
내부 필드를 업데이트하려면 concat 연산자를 사용하지 마십시오 ||
. 대신 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 함수 문서 에서 전력을 찾으십시오 . #-
운영자, jsonb_set
기능 및 기능에 관심이있을 수 있습니다 jsonb_insert
.
답변
@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$;
키별 재귀 삭제 : 키 경로로 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"}}
답변
UPDATE test
SET data = data::jsonb - 'a' || '{"a":5}'::jsonb
WHERE data->>'b' = '2'
이것은 PostgreSQL 9.5에서 작동하는 것 같습니다
답변
필드 유형이 json 인 경우 다음이 적합합니다.
UPDATE
table_name
SET field_name = field_name::jsonb - 'key' || '{"key":new_val}'
WHERE field_name->>'key' = 'old_value'.
연산자 ‘-‘는 왼쪽 피연산자에서 키 / 값 쌍 또는 문자열 요소를 삭제합니다. 키 / 값 쌍은 해당 키 값을 기준으로 일치합니다.
연산자 ‘||’ 두 개의 jsonb 값을 새로운 jsonb 값으로 연결합니다.
이들은 jsonb 연산자이므로 다음과 같이 typecast해야합니다. : jsonb
추가 정보 : JSON 함수 및 연산자
답변
PostgreSQL 9.4에서는 다음과 같은 파이썬 함수를 구현했습니다. 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;
이전 고용주, I는 (안으로서 텍스트로 JSON 데이터를 조작하기위한 C 함수 세트를 쓴 참고 json
하거나 jsonb
, 예를 들어 PostgreSQL의 7, 8 및 9에 대해 타입)으로 데이터를 추출 json_path('{"obj":[12, 34, {"num":-45.67}]}', '$.obj[2]['num']')
하여 데이터를 설정 json_path_set('{"obj":[12, 34, {"num":-45.67}]}', '$.obj[2]['num']', '99.87')
등등. 작업에 약 3 일이 걸렸으므로 레거시 시스템에서 실행하고 여유 시간이 필요한 경우에는 노력할 가치가 있습니다. C 버전이 파이썬 버전보다 훨씬 빠르다고 생각합니다.