JSON 可謂風靡互聯網,在數據交換使用上,其優點特別明顯,其結構簡潔、可讀易讀、形式靈活。不少 API 接口的數據都採用 JSON 來表示。html
PostgreSQL 對 JSON 提供了良好的支持。具體的相關函數可參考:JSON類型和函數sql
從使用的角度而言,我的以爲常見的應用場景爲:數據庫
- 讀取單個 JSON 的屬性值
- 遍歷單個 JSON 的全部屬性
- 遍歷一個 JSON 數組
- 建立一個 JSON 做爲返回值
之因此僅這些簡單的場景,緣由在於,在應用中使用高級語言處理 JSON 與在數據庫中使用那些高級的 JSON 函數相比,從操做上和可讀性上均爽不少。在不支持 JSON 的數據庫中,咱們也常使用單個文本字段存儲 JSON 字符串,而後在應用中加以解析處理。json
咱們以如下的 JSON 字符串做爲輸入,來了解 PostgreSQL 在各場景中的應用實現。api
{ "label": { "names": ["Amy", "Kala", "Lily"] }, "color": "red", "count": 3 } // 寫成一行便是 {"label":{"names":["Amy","Kala","Lily"]},"color":"red","count":3}
首先,經過下面的表格,感覺一下 JavaScript 與 PostgreSQL 中讀取 color 屬性與 label 屬性中 names 的第二個值的形式。數組
讀屬性 | JavaScript | PostgreSQL |
---|---|---|
定義 | var jsonObj = {"label":{"names":["Amy","Kala","Lily"]}, "color":"red","count":3}; | jsonObj := '{"label":{"names":["Amy","Kala","Lily"]}, "color":"red","count":3}'::json; |
讀取 JSON 的 color 屬性 | jsonObj.color | jsonObj -> 'color' |
讀取 JSON 的 label 中 names 的第二個值 | jsonObj.label.names[1] | jsonObj -> 'label' -> 'names' -> 1 |
在 PostgreSQL 中咱們可使用如下語句逐層指定屬性路徑(屬性名稱須要使用字符串需單引號,數組索引使用數字)來獲取值:函數
-- 取得 color 屬性 SELECT '{"label":{"names":["Amy","Kala","Lily"]},"color":"red","count":3}'::json -> 'color'; -- 取得 label 屬性下的 names 的第二個值 SELECT '{"label":{"names":["Amy","Kala","Lily"]},"color":"red","count":3}'::json -> 'label' -> 'names' -> 1;
此時取得的值仍然爲 json 類型,若是須要取得值的文本形式,則把最後一個 "->" 變成 "->>" 便可。ui
固然,路徑的表示,也能夠經過 #> '{label,names,1}' 的形式表示:code
SELECT '{"label":{"names":["Amy","Kala","Lily"]},"color":"red","count":3}'::json #> '{label,names,1}';
使用 json_each 函數,便可返回屬性鍵值對的數據集,數據集包括兩列,key 表示屬性,value 表示屬性值。以下語句輸出全部結果:htm
DO $$ DECLARE lv_row record; jsonObj json := '{"label":{"names":["Amy","Kala","Lily"]},"color":"red","count":3}'::json; BEGIN FOR lv_row IN SELECT * FROM json_each(jsonObj) LOOP raise notice 'key is %, value is %', lv_row.key, lv_row.value; END LOOP; END $$;
輸出
NOTICE: key is label, value is {"names":["Amy","Kala","Lily"]}
NOTICE: key is color, value is "red"
NOTICE: key is count, value is 3
經過使用 json_array_length 函數獲取數組的長度,而後根據索引遍歷整個數組便可。
DO $$ DECLARE lv_row record; lv_size int; jsonObj json := '{"label":{"names":["Amy","Kala","Lily"]},"color":"red","count":3}'::json; BEGIN -- 取得label 下names 這個json數組 jsonObj := jsonObj #> '{label,names}'; -- 取得數組的長度 lv_size := json_array_length(jsonObj); -- 按索引遍歷整個數組 FOR i IN 0..lv_size-1 LOOP raise notice '%', jsonObj -> i; END LOOP; END $$;
輸出:
NOTICE: "Amy"
NOTICE: "Kala"
NOTICE: "Lily"
使用 json_build_object 函數,傳遞 key, value 成對的參數便可建立一個 json,如如下語句造成一個 api 經常使用的返回執行狀況的 json。
SELECT json_build_object('code', 200, 'err_msg', 'run success!');
咱們以填寫學生地址爲例,傳遞給存儲過程的是一個 json 數組,每一個數組中的 json 對象包括了學生標識與地址信息。
如下語句建立數據表
-- student_id 學生標識, address 地址 CREATE TABLE student_address (student_id varchar(10) PRIMARY KEY, address varchar(100));
如下爲處理過程
CREATE OR REPLACE FUNCTION save_student_addresses_json( v_array_json json) RETURNS json LANGUAGE 'plpgsql' AS $$ DECLARE lv_row_json json; lv_length int; lv_field_student_id varchar; lv_field_address varchar; BEGIN -- 取得數組的長度 lv_length := json_array_length(v_array_json); FOR i IN 0..lv_length-1 LOOP -- 取得第 i 行的 json 值 lv_row_json := v_array_json -> i; lv_field_student_id := lv_row_json ->> 'student_id'; lv_field_address := lv_row_json ->> 'address'; -- 插入學生地址信息,若是存在則更新地址 INSERT INTO student_address (student_id, address) VALUES (lv_field_student_id, lv_field_address) ON CONFLICT (student_id) DO UPDATE SET address = excluded.address; END LOOP; RETURN json_build_object( 'err_code', 200, 'err_msg', '保存或更新 ' || lv_length || ' 條記錄' ); end $$
咱們執行如下操做
SELECT save_student_addresses_json( '[ {"student_id":"01","address":"街道A"}, {"student_id":"02","address":"街道B"} ]' );
運行結果:{"err_code":200,"err_msg":"保存或更新 2 條記錄"}
PostgreSQL 對 JSON 的操做支持特性很豐富,但文檔中那麼多函數一下映入眼簾,讓人以爲複雜凌亂。本文從簡單易理解的幾個應用場景出發,但願能先爽上一把,然後再細細深刻。I love PostgreSQL!