2014-11-02 120 views
49

通過查看Postgres 9.4數據類型JSONB的文檔,我不明白如何在JSONB列上執行更新。如何在Postgres中對JSONB類型的列執行更新操作9.4

的文檔JSONB類型和功能:

http://www.postgresql.org/docs/9.4/static/functions-json.html http://www.postgresql.org/docs/9.4/static/datatype-json.html

作爲一個例子,我有這樣的基本表結構:

CREATE TABLE test(id serial, data jsonb); 

插入是容易的,因爲在:

INSERT INTO test(data) values ('{"name": "my-name", "tags": ["tag1", "tag2"]}'); 

現在,我將如何更新'數據'列?這是無效的語法:

UPDATE test SET data->'name' = 'my-other-name' WHERE id = 1; 

這是記錄在某處明顯,我錯過了?謝謝。

回答

22

理想情況下,您不使用JSON文檔處理要在關係數據庫內部管理的數據。改爲使用歸一化關係設計

JSON主要用於存儲不需要在RDBMS內部操作的整個文檔。

更新Postgres中的一行總是會寫入新版本的整個行。這是Postgres' MVCC model的基本原則。從性能的角度來看,您是否更改JSON對象內的單個數據或全部數據並不重要:必須編寫新版本的行。

因此advice in the manual:存儲在表中時

JSON數據受到相同的併發控制的考慮 任何其它數據類型。雖然存儲大型文檔是可行的,但請記住,任何更新都會在整行上獲取行級鎖定 。考慮將JSON文檔限制爲 可管理的大小,以便減少更新 事務之間的鎖爭用。理想情況下,JSON文檔應該分別表示業務規則規定的原子數據,該數據不能被合理地細分爲可以獨立修改的更小的數據。

它的要點:修改什麼一個JSON對象的內部,你必須修改的對象分配給列。除了存儲功能之外,Postgres還提供有限的手段來構建和操縱數據。自9.2版以來,隨着每個新版本的發佈,工具庫大幅增長。但主要依然是:你總是必須分配一個完整的修改對象列和Postgres總是寫一個新的行版本的任何更新。

一些技巧如何使用Postgres 9的工具。3或更高版本:

+2

此答案只關注JSON類型並忽略JSONB。 – fiatjaf 2015-07-15 14:09:37

+4

@fiatjaf:這個答案完全適用於'json'和'jsonb'數據類型。兩者都存儲JSON數據,'jsonb'以規範化的二進制形式存在,具有一些優點(並且缺點很少)。 http://stackoverflow.com/a/10560761/939860兩種數據類型都不適合在數據庫中操作*。 ***沒有***文件類型是。那麼,對於小型的,幾乎沒有結構化的JSON文檔來說很好。但是,大的嵌套文件將是一個愚蠢的方式。 – 2015-07-15 14:26:56

+0

這就爲我澄清了答案。謝謝。 – fiatjaf 2015-07-15 14:50:00

22

這在9.5即將在jsonb_set通過Andrew Dunstan基於現有擴展jsonbx形式,它與9.4

+0

這一行的另一個問題是使用['jsonb_build_object()'](http://www.postgresql.org/docs/因爲'x-> key',不返回鍵對象對,爲了填充你需要''jsonb_set(target,path,jsonb_build_object('key',x-> key))' 。 – 2016-05-02 21:43:01

9

這個問題的工作被要求在postgres的上下文9.4, 然而,新來的這個問題的觀衆應該知道,在postgres 9.5, 子文檔JSONB上創建/更新/刪除操作字段由數據庫本地支持,而不需要擴展功能。

參見:JSONB modifying operators and functions

2

可能: 更新測試組數據= ' 「我-其他名稱」' :: JSON WHERE ID = 1;

它的工作與我的情況下,如果數據是JSON類型

+1

也爲我工作,在postgresql 9.4.5上。整個記錄被重寫,所以不能更新單個字段atm。 – kometen 2015-11-10 10:59:14

93

如果你能夠升級到PostgreSQL 9.5,該jsonb_set命令可用,正如其他人提及。

在每個後面的SQL語句中,爲了簡潔起見,我省略了where子句;顯然,你想要補充一點。

更新名稱:

UPDATE test SET data = jsonb_set(data, '{name}', '"my-other-name"'); 

更換標籤(如反對添加或刪除標記):

UPDATE test SET data = jsonb_set(data, '{tags}', '["tag3", "tag4"]'); 

更換第二標籤(0索引):

UPDATE test SET data = jsonb_set(data, '{tags,1}', '"tag5"'); 

追加一個標籤( )只要有少於999個標籤,這將工作;將參數999更改爲100 0或以上生成一個錯誤 。這在Postgres 9.5.3中似乎不再是這種情況;一個更大的索引都可以使用):

UPDATE test SET data = jsonb_set(data, '{tags,999999999}', '"tag6"', true); 

刪除最後一個標籤:

UPDATE test SET data = data #- '{tags,-1}' 

複雜的更新(刪除最後一個標籤,插入一個新的標籤,並更改名稱):

UPDATE test SET data = jsonb_set(
    jsonb_set(data #- '{tags,-1}', '{tags,999999999}', '"tag3"', true), 
    '{name}', '"my-other-name"'); 

需要注意的是,在每個示例中,實際上並未更新JSON數據的單個字段。相反,您正在創建數據的臨時修改版本,並將修改後的版本分配回該列。實際上,結果應該是一樣的,但記住這一點應該使複雜的更新,如最後一個例子,更容易理解。

在複雜的例子中,有三個轉換和三個臨時版本:首先,最後一個標籤被刪除。然後,通過添加新標籤來轉換該版本。接下來,通過更改name字段來變換第二個版本。 data列中的值將被最終版本替換。

+5

您可以獲得獎勵積分,用於顯示如何更新表格中的列作爲OP請求 – chadrik 2016-07-13 05:28:47

+0

我知道這不是原始請求中的內容,但我很想看到如何鏈接這些內容的示例。刪除兩個鍵並在一個UPDATE中設置兩個鍵。 – chadrik 2016-07-13 05:35:52

+1

@chadrik:我添加了一個更復雜的例子。它並不完全符合你的要求,但它應該給你一個想法。請注意,外部'jsonb_set'調用的輸入是來自內部調用的輸出,並且該內部調用的輸入是'data# - '{tags,-1}'的結果。即,刪除了最後一個標記的原始數據。 – Jimothy 2016-07-13 18:40:50

9

對於那些運行到這個問題,並希望有一個非常快速修復(並停留在9.4.5或更早),這裏是我做過什麼:

創建測試表的

CREATE TABLE test(id serial, data jsonb); 
INSERT INTO test(data) values ('{"name": "my-name", "tags": ["tag1", "tag2"]}'); 

更新語句更改名稱jsonb財產

UPDATE test 
SET data = replace(data::TEXT,'"name":','"my-other-name"')::jsonb 
WHERE id = 1; 

最終,一個被接受的答案是正確的,因爲你不能修改jsonb對象的單個片段(在9.4.5或更早版本中);但是,您可以將jsonb對象轉換爲一個字符串(:: TEXT),然後操縱該字符串並將其轉換回jsonb對象(:: jsonb)。

有兩個重要的注意事項

  1. 這將在JSON替換所有屬性叫做「名」(在的情況下你有多個同名的屬性)
  2. 這是效率不高作爲jsonb_set如果您正在使用9.5

雖這麼說是,我遇到了一個情況,我不得不更新在jsonb對象內容的模式,這是實現E中的最簡單方法xactly原始海報的要求。

+0

好主,我一直在尋找如何做兩個小時更新jsonb,所以我可以替換所有'\ u0000'空字符,例如顯示完整的圖片。感謝你! – 2016-09-19 17:06:05

+0

看起來不錯! btw在你的例子中替換的第二個參數包含冒號,第三個不包含冒號。看起來你的調用應該是'replace(data :: TEXT,''name':','「my-other-name」:'):: jsonb' – davidicus 2017-01-09 14:23:21

3

我爲我自己寫了一個小函數,在Postgres 9.4中遞歸地工作。我有同樣的問題(他們確實解決了Postgres 9.5中的一些頭痛問題)。 反正這裏是函數(我希望它可以很好地用於你):

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) 

正如你可以看到它分析深處,並根據需要更新/添加值。

相關問題