pgsql json,jsonb
關於pgsql 的json 和jsonb 的資料處理筆記
json 和jsonb 區別
兩者從使用者操作的角度來說沒有區別,區別主要是儲存和讀取的系統處理(預處理)和耗時方面有區別。json寫入快,讀取慢,jsonb寫入慢,讀取快。常用的操作符
操作符:
-> // 右邊傳入整數(針對純陣列),獲取陣列的第n個元素,n從0開始算,返回值為json
示例: select '[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json->2 // 輸出 {"c":"baz"}
-> // 右邊傳入鍵值(針對關聯陣列),獲取陣列的第n個元素,n從0開始算,返回值為json
示例: select '{"a": {"b":"foo"}, "c":{"a": "aaa"}}'::json->'a' // 輸出 {"b":"foo"}
->> // 右邊傳入整數(針對純陣列),獲取陣列的第n個元素,n從0開始算,返回值為文字
示例: select '[{"a":"foo"},{"b":"bar"},{"c":"baz"}]'::json->>2 // 輸出 {"c":"baz"}
->> // 右邊傳入鍵值(針對關聯陣列),獲取陣列的第n個元素,n從0開始算,返回值為文字
示例:select '{"a": {"b":"foo"}, "c":{"a": "aaa"}}'::json->>'a' // 輸出 {"b":"foo"}
// 獲取json子物件,傳入陣列,返回json
示例: select '{"a": {"b":{"c": "foo"}}}'::json#> '{a,b}' // 輸出 {"c": "foo"}
// 獲取json子物件並轉換為文字,傳入陣列,返回文字
示例: select '{"a": {"b":{"c": "foo"}}}'::json#>> '{a,b}' // 輸出 {"c": "foo"}
- 操作函式
目前pgsql版本提供了兩套函式分別處理,可以通用,名稱也差不多,比如 json_each 和 jsonb_each , json_array_elements 和 jsonb_array_elements 。
json相關的處理函式比較多,常用的有如下三個,這三個基本夠用了
json_object_keys // 返回json的鍵(多層只返回第一層),該函式不能用於純陣列.
json_array_elements // 提取轉換純陣列元素
json_extract_path // 返回JSON值所指向的某個鍵元素(相當於 #> 操作符),該函式不能直接操作純陣列。
需要注意的是如果你建立欄位用的是json就用json相關函式,如果建立欄位用的是jsonb就用jsonb相關函式。
json_object_keys 函式示例:
select json_object_keys ('
{
"goods":
[
{"id": "676a13d3-0225-4431-b858-678c3cfeab74", "weight": "1", "quantity": "9999999"},
{"id": "111a13d3-0225-4431-b858-678c3cfeab75", "weight": "2", "quantity": "33"}
],
"quantity": "10"
}
')
輸出:
json_object_keys 函式示例:
select json_object_keys ('
[
{"id": "676a13d3-0225-4431-b858-678c3cfeab74", "weight": "1", "quantity": "9999999"},
{"id": "111a13d3-0225-4431-b858-678c3cfeab75", "weight": "2", "quantity": "33"}
],
')
輸出:
ERROR: cannot call json_object_keys on an array // 不能用於陣列
json_array_elements 函式 示例:
select json_array_elements ('
[
{"id": "676a13d3-0225-4431-b858-678c3cfeab74", "weight": "1", "quantity": "9999999"},
{"id": "111a13d3-0225-4431-b858-678c3cfeab75", "weight": "2", "quantity": "33"},
{"id": "111a13d3-0225-4431-b858-678c3cfea999", "weight": "3", "quantity": "11"}
]
')
我們看到json資料被分離成三條記錄,這時我們就可以對其進行查詢操作,
比如查詢是否包含了weight=3的資料。
select * from json_array_elements ('
[
{"id": "676a13d3-0225-4431-b858-678c3cfeab74", "weight": "1", "quantity": "9999999"},
{"id": "111a13d3-0225-4431-b858-678c3cfeab75", "weight": "2", "quantity": "33"},
{"id": "111a13d3-0225-4431-b858-678c3cfea999", "weight": "3", "quantity": "11"}
]
') as jae
where jae::jsonb->>'weight' = '3'
輸出:
我們看到這樣就可以到對json資料內部進行查詢了。
json_extract_path 函式示例:
比如要獲取鍵 ‘goods’ 的值:
{
"goods":
[
{"id": "676a13d3-0225-4431-b858-678c3cfeab74", "weight": "1", "quantity": "9999999"},
{"id": "111a13d3-0225-4431-b858-678c3cfeab75", "weight": "2", "quantity": "33"}
],
"quantity": {"max": "150", "min": "2"}
}
select json_extract_path ('
{
"goods":
[
{"id": "676a13d3-0225-4431-b858-678c3cfeab74", "weight": "1", "quantity": "9999999"},
{"id": "111a13d3-0225-4431-b858-678c3cfeab75", "weight": "2", "quantity": "33"}
],
"quantity": {"max": "150", "min": "2"}
}
' , 'goods' ) ; // 第二個引數表示獲取鍵為goods的值
輸出:
json_extract_path 函式和 pgsql 提供的操作符 #> 是一樣的。
select ('
{
"goods":
[
{"id": "676a13d3-0225-4431-b858-678c3cfeab74", "weight": "1", "quantity": "9999999"},
{"id": "111a13d3-0225-4431-b858-678c3cfeab75", "weight": "2", "quantity": "33"}
],
"quantity": {"max": "150", "min": "2"}
}
') ::json #> '{goods}'
兩者的輸出是一致的。
同樣我們要輸出 鍵quantity 下鍵max 的值:
select json_extract_path ('
{
"goods":
[
{"id": "676a13d3-0225-4431-b858-678c3cfeab74", "weight": "1", "quantity": "9999999"},
{"id": "111a13d3-0225-4431-b858-678c3cfeab75", "weight": "2", "quantity": "33"}
],
"quantity": {"max": "150", "min": "2"}
}
' , 'quantity','max' ) ;
-- 或
select ('
{
"goods":
[
{"id": "676a13d3-0225-4431-b858-678c3cfeab74", "weight": "1", "quantity": "9999999"},
{"id": "111a13d3-0225-4431-b858-678c3cfeab75", "weight": "2", "quantity": "33"}
],
"quantity": {"max": "150", "min": "2"}
}
') ::json #> '{quantity, max}
兩者輸出是一樣的。
這幾個函式是可以聯合使用的。
比如我們要查詢 鍵“goods” 下weight =2 的id 和quantity 值,語句如下:
select jae::json->>'id' as id, jae::json->>'quantity' as quantity from json_array_elements (
json_extract_path ('
{
"goods":
[
{"id": "676a13d3-0225-4431-b858-678c3cfeab74", "weight": "1", "quantity": "9999999"},
{"id": "111a13d3-0225-4431-b858-678c3cfeab75", "weight": "2", "quantity": "33"}
],
"quantity": {"max": "150", "min": "2"}
}
' , 'goods' ) ) as jae where jae::json->> 'weight' = '2'
輸出:
上述的json語句我們可以當做欄位來使用,就相當於對錶記錄進行操作了。
接下來我們同個一個例子講解json在表中的用法:
示例:查詢表中jsonb_msg欄位中goods下id=1003和1002的記錄,表中輸入如下圖:
sql查詢語句:
select name,* from upgrade_test.test1 test
where
( select count(*) from jsonb_array_elements (
jsonb_extract_path(test.json_msg , 'goods' ) ) as jae where jae::json->> 'id' in ('1001','1003') ) > 0 ;
輸出:
效率還行:
Total query runtime: 11 msec
檢索到 2 行。
官方文件頁:
PostgreSQL9.4 新增 JSONB 資料型別, JSONB 同時屬於 JSON (JavaScript Object Notation) 資料型別,jsonb 和 json 的輸入資料幾乎完全通用,最大的差別體現在效率上,json 儲存的資料幾乎和輸入資料一樣,儲存的是未解析的資料,呼叫函式時使用效率較低; 而 jsonb 儲存的是分解的 binary 格式資料,使用時不需要再解析了,因此使用上效率較高; 另一方面 json 在寫入時較快,而 jsonb 寫入時由於需要轉換導致寫入較慢。下面通過些簡單的例子瞭解兩者的差異。
–1 這個例子兩者沒啥差異
francs=> SELECT '[1, 2, "foo", null]'::json;
json
[1, 2, “foo”, null]
(1 row)
francs=> SELECT '[1, 2, "foo", null]'::jsonb;
jsonb
[1, 2, “foo”, null]
(1 row)
備註: json 型別輸出的內容和寫入的內容一樣,不會對輸出的結果改變,而 jsonb不一樣,看下面的例子。
–2 jsonb 輸出內容順序不一樣
francs=> SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::json;
json
{“bar”: “baz”, “balance”: 7.77, “active”:false}
(1 row)
francs=> SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::jsonb;
jsonb
{“bar”: “baz”, “active”: false, “balance”: 7.77}
(1 row)
–3 jsonb: 整數型別輸出不一樣
francs=> SELECT '{"reading": 1.230e-5}'::json, '{"reading": 1.230e-5}'::jsonb;
json | jsonb
———————–+————————-
{“reading”: 1.230e-5} | {“reading”: 0.00001230}
(1 row)
–4 jsonb: 去掉了空格
francs=> select ' {"id":1,
"name":"francs",
"remark":"a good guy!"
}'::json;
json
{“id”:1, +
“name”:”francs”, +
“remark”:”a good guy!”+
}
(1 row)
francs=> select ' {"id":1,
"name":"francs",
"remark":"a good guy!"
}'::jsonb;
jsonb
{“id”: 1, “name”: “francs”, “remark”: “a good guy!”}
(1 row)
–5 jsonb: 重複的元素值僅保留最後一個
francs=> select ' {"id":1,
"name":"francs",
"remark":"a good guy!",
"name":"test"
}'::jsonb;
jsonb
{“id”: 1, “name”: “test”, “remark”: “a good guy!”}
(1 row)
備註: json 型別的輸出和輸入一樣,會保留所有重複的元素,而 jsonb 對於重複的元素僅保留最後出現的重複元素。
–6 關於索引
GIN 索引支援 jsonb 型別,支援大的 jsonb 表中基於 keys 或者 key/values 模式的檢索。
預設的 GIN 索引模式支援帶有 @>, ?, ?& 和 ?| 操作的查詢,關於這些操作符的含義參考本文的附錄。
假如有一個文件:
{
"guid": "9c36adc1-7fb5-4d5b-83b4-90356a46061a",
"name": "Angela Barton",
"is_active": true,
"company": "Magnafone",
"address": "178 Howard Place, Gulf, Washington, 702",
"registered": "2009-11-07T08:53:22 +08:00",
"latitude": 19.793713,
"longitude": 86.513373,
"tags": [
"enim",
"aliquip",
"qui"
]
}
我們將表名定義為 api, jsonb 欄位為 jdoc,建立如下索引
CREATE INDEX idx_gin_api_jdoc ON api USING gin (jdoc);
那麼如下的查詢可以使用索引
-- Find documents in which the key "company" has value "Magnafone"
SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"company": "Magnafone"}';
備註:上面這個例子來自手冊。
7 附 Additional jsonb Operators
Operator | Right Operand Type | Description | Example |
---|---|---|---|
= | jsonb | Are the two JSON values equal? | ‘[1,2,3]’::jsonb = ‘[1,2,3]’::jsonb |
@> | jsonb | Does the left JSON value contain within it the right value? | ‘{“a”:1, “b”:2}’::jsonb @> ‘{“b”:2}’::jsonb |
<@ | jsonb | Is the left JSON value contained within the right value? | ‘{“b”:2}’::jsonb <@ ‘{“a”:1, “b”:2}’::jsonb |
? | text | Does the key/element string exist within the JSON value? | ‘{“a”:1, “b”:2}’::jsonb ? ‘b’ |
? | text[] | Do any of these key/element strings exist? | |
?& | text[] | Do all of these key/element strings exist? | ‘[“a”, “b”]’::jsonb ?& array[‘a’, ‘b’] |
8 參考
WAITING FOR 9.4 – INTRODUCE JSONB, A STRUCTURED FORMAT FOR STORING JSON
PostgreSQL 9.4 Beta 1釋出,支援JSONB
PostgreSQL 9.4 new data type jsonb - do not need to reparser when used
JSON Types