如何更新Postgresql的Jsonb數組

如何更新Postgresql的Jsonb數組

假設你決定將數據以json或者jsonb的形式存儲在數據庫中,然後發現你剛剛給自己製造了新的問題,而這些問題是以前沒有的。

jsonb是一個強大的工具,但它也有一定的代價,因爲你需要調整查詢和處理數據的方式。

而且將整個jsonb對象加載到內存中,用你喜歡的編程語言進行轉換,然後將其保存回數據庫,這並不罕見。但是,你剛剛創造了另一個問題:性能瓶頸和資源浪費。

在這篇文章中,我們來看看如何通過一次查詢來更新數組內對象的特定值。

假設你正在實現一個爲每個客戶存儲動態聯繫人功能,那麼你就會想到將聯繫人存儲爲jsonb列,因爲他們是動態的,因此使用非關係型數據結構是有意義的。

然後創建一個帶有jsonb列聯繫人,並在其中插入一些數據。

create table customers (name varchar(256), contacts jsonb);

insert into customers (name, contacts) values (
  'Jimi',
  '[
    {"type": "phone", "value": "+1-202-555-0105"},
    {"type": "email", "value": "[email protected]"}
  ]'
);

insert into customers (name, contacts) values (
  'Janis',
  '[
	{"type": "email", "value": "[email protected]"}
   ]'
);

看起來很簡單,但是如何更新特定的聯繫人信息?如何更改jimi的電子郵件或者janis的電話?

幸運的是,PostgreSQL提供了json_set函數。函數原型:

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

給定一個jsonb列,可以在指定的路徑上設置一個新值。

select jsonb_set(
  '[{"type": "phone", "value": "+1-202-555-0105"},{"type": "email", "value": "[email protected]"}]',
  '{1,value}',
  '"[email protected]"',
  false
);

select jsonb_set(
  '[{"type": "email", "value": "[email protected]"}]',
  '{0,value}',
  '"[email protected]"',
  false
);

jsonb_set函數的第一個參數爲jsonb數據對象,第二個參數路徑,在上面的例子中,{1,value}爲數組中索引爲1的元素,並且屬性是value字段的,最後一個參數是對路徑中選擇的字段進行賦值。

根據這樣的規則,那麼第一個sql語句就是修改jimi的郵箱,而第二個sql語句就是修改janis的電子郵箱。

上面返回的結果是:

[{"type":"phone","value":"+1-202-555-0105"},{"type":"email","value":"[email protected]"}]
[{"type":"email","value":"[email protected]"}]

如果現在你認爲這樣就完事了,那就是你太Too young! Too simple!

非關係型數據庫的問題在於它們是動態的,這也是使用jsonb的原因之一,但是這就帶來了一個問題,例如上面的案例,jimi的郵箱對象在數組中的索引是1,janis的郵箱對象在數組中索引是0,而另外的一條數據很可能是不同的數組,其索引也不一樣,那麼如何確定每個聯繫人的郵箱所在數組的索引?

答案 是對數組中的元素進行排序,並獲得索引。

select index-1 as index
  from customers
      ,jsonb_array_elements(contacts) with ordinality arr(contact, index)
 where contact->>'type' = 'email'
   and name = 'Jimi';

該查詢會返回1,這是jimi聯繫人的電子郵件對象索引。

現在萬事俱備,只欠東風!我們把查詢和更改步驟合併。

with contact_email as (
  select ('{'||index-1||',value}')::text[] as path
    from customers
        ,jsonb_array_elements(contacts) with ordinality arr(contact, index)
   where contact->>'type' = 'email'
     and name = 'Jimi'
)
update customers
   set contacts = jsonb_set(contacts, contact_email.path, '"[email protected]"', false)
  from contact_email
 where name = 'Jimi';

這個sql中最重要的部分就是with,這是一個強大的命令,但對於這個例子來說,你可以把它看成是“儲存變量”功能,with最後的結果都儲存在contact_email變量內,其中就包含需要更新的路徑,也就是jimi的郵箱路徑。

下面再稍微詳細介紹一下:

(‘{‘||index-1||’,value}’)::text[] as path

這一段是在建立路徑{1, value},但是要轉換成text[]類型,因爲jsonb_set函數需要這個類型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章