2016-06-28 78 views
2

我正在使用PostgreSQL 9.4.5。我想更新 a jsonb列。如何用PostgreSQL更新jsonb字符串?

我的表的結構是這樣的:

CREATE TABLE my_table (
    gid serial PRIMARY KEY, 
    "data" jsonb 
); 

JSON字符串是這樣的:

{"files": [], "ident": {"id": 1, "country": null, "type ": "20"}} 

下面的SQL不會做的工作(語法錯誤 - SQL狀態= 42601):

UPDATE my_table SET "data" -> 'ident' -> 'country' = 'Belgium'; 

有沒有辦法做到這一點?

+1

'UPDATE my_table SET「data」= jsonb_set(「data」,'{「ident」,「country」}','「Belgium」');' – Abelisto

+0

實際重複:[如何修改字段在新的PostgreSQL JSON數據類型?](http://stackoverflow.com/q/18209625/593144) – Abelisto

+0

我同意,但在帖子中的問題你的指的是關於'json'類型(而不是'jsonb')。其他答案廣泛指向'jsonb'。 – wiltomap

回答

-1

我以前的解決方案依賴於9.5功能。

我會推薦使用下面的abelisto的解決方案,或者使用pl/perlu,plpythonu或plv8js來編寫json mutators,使其更好地支持它們。

+0

您的查詢返回錯誤消息:* fonction jsonb_build_object(未知,未知)不存在* – wiltomap

+0

抱歉,誤讀了文檔,應爲json_build_object,然後將最終結果轉換爲jsonb。編輯。 –

+0

hmmm ||也只有9.5。 –

0

在PG 9.4中,您運用「簡單」解決方案(如jsonb_set()(9.5))運氣不佳。唯一的選擇是解壓JSON對象,進行更改並重新構建對象。這聽起來非常麻煩,而且的確如此:無論先進程度如何或精心設計內置函數,JSON都難以操作。

CREATE TYPE data_ident AS (id integer, country text, "type" integer); 

UPDATE my_table 
SET "data" = json_build_object('files', "data"->'files', 'ident', ident.j)::jsonb 
FROM (
    SELECT gid, json_build_object('id', j.id, 'country', 'Belgium', 'type', j."type") AS j 
    FROM my_table 
    JOIN LATERAL jsonb_populate_record(null::data_ident, "data"->'ident') j ON true) ident 
WHERE my_table.gid = ident.gid; 

SELECT條款"data"->'ident'被解壓到一個記錄(您需要CREATE TYPE的結構)。然後將其重新構建到帶有新國家名稱的JSON對象中。在UPDATE中,"ident"對象與"files"對象重新合併,整個事件投射到jsonb

美的純粹的東西 - 只要把握速度是不是你的事......

+0

IMO你的解決方案的主要缺點是你重新構建整個對象而不關注其可能不同的初始結構。 – Abelisto

1

好有兩個功能:

create or replace function set_jsonb_value(p_j jsonb, p_key text, p_value jsonb) returns jsonb as $$ 
    select jsonb_object_agg(t.key, t.value) from (
    select 
     key, 
     case 
     when jsonb_typeof(value) = 'object' then set_jsonb_value(value, p_key, p_value) 
     when key = p_key then p_value 
     else value 
     end as value from jsonb_each(p_j)) as t; 
$$ language sql immutable; 

第一個只是改變了現有鍵的值不管關鍵路徑:

postgres=# select set_jsonb_value(
    '{"files": [], "country": null, "ident": {"id": 1, "country": null, "type ": "20"}}', 
    'country', 
    '"foo"'); 
            set_jsonb_value          
-------------------------------------------------------------------------------------- 
{"files": [], "ident": {"id": 1, "type ": "20", "country": "foo"}, "country": "foo"} 
(1 row) 


create or replace function set_jsonb_value(p_j jsonb, p_path text[], p_value jsonb) returns jsonb as $$ 
    select jsonb_object_agg(t.key, t.value) from (
    select 
     key, 
     case 
     when jsonb_typeof(value) = 'object' then set_jsonb_value(value, p_path[2:1000], p_value) 
     when key = p_path[1] then p_value 
     else value 
     end as value from jsonb_each(p_j) 
    union all 
    select 
     p_path[1], 
     case 
     when array_length(p_path,1) = 1 then p_value 
     else set_jsonb_value('{}', p_path[2:1000], p_value) end 
    where not p_j ? p_path[1]) as t; 
$$ language sql immutable; 

第二個改變使用指定的路徑已經存在的鍵的值,或者如果路徑不存在,創建它:

postgres=# select set_jsonb_value(
    '{"files": [], "country": null, "ident": {"id": 1, "type ": "20"}}', 
    '{ident,country}'::text[], 
    '"foo"'); 
            set_jsonb_value         
------------------------------------------------------------------------------------- 
{"files": [], "ident": {"id": 1, "type ": "20", "country": "foo"}, "country": null} 
(1 row) 

postgres=# select set_jsonb_value(
    '{"files": [], "country": null, "ident": {"id": 1, "type ": "20"}}', 
    '{ident,foo,bar,country}'::text[], 
    '"foo"'); 
              set_jsonb_value            
------------------------------------------------------------------------------------------------------- 
{"files": [], "ident": {"id": 1, "foo": {"bar": {"country": "foo"}}, "type ": "20"}, "country": null} 
(1 row) 

希望它會幫助的人誰使用PostgreSQL的< 9.5
免責聲明:測試on PostgreSQL 9.5