PostgreSQL 務(wù)實應(yīng)用(四/5)JSON

JSON 可謂風(fēng)靡互聯(lián)網(wǎng)颜骤,在數(shù)據(jù)交換使用上,其優(yōu)勢特別明顯捣卤,其結(jié)構(gòu)簡潔忍抽、可讀易讀八孝、形式靈活。很多 API 接口的數(shù)據(jù)都采用 JSON 來表示鸠项。

PostgreSQL 對 JSON 提供了良好的支持干跛。具體的相關(guān)函數(shù)可參考:JSON類型和函數(shù)

從使用的角度而言,個人覺得常見的應(yīng)用場景為:

  1. 讀取單個 JSON 的屬性值
  2. 遍歷單個 JSON 的所有屬性
  3. 遍歷一個 JSON 數(shù)組
  4. 創(chuàng)建一個 JSON 作為返回值

之所以僅這些簡單的場景祟绊,原因在于楼入,在應(yīng)用中使用高級語言處理 JSON 與在數(shù)據(jù)庫中使用那些高級的 JSON 函數(shù)相比,從操作上和可讀性上均爽很多牧抽。在不支持 JSON 的數(shù)據(jù)庫中嘉熊,我們也常使用單個文本字段存儲 JSON 字符串,然后在應(yīng)用中加以解析處理扬舒。

四個場景

我們以以下的 JSON 字符串作為輸入阐肤,來了解 PostgreSQL 在各場景中的應(yīng)用實現(xiàn)。

{
    "label": {
        "names": ["Amy", "Kala", "Lily"]
    },
    "color": "red",
    "count": 3
}

// 寫成一行即是
{"label":{"names":["Amy","Kala","Lily"]},"color":"red","count":3}

1.讀取屬性

首先呼巴,通過下面的表格,感受一下 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 中我們可以使用以下語句逐層指定屬性路徑(屬性名稱需要使用字符串需單引號衣赶,數(shù)組索引使用數(shù)字)來獲取值:

-- 取得 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 類型,如果需要取得值的文本形式厚满,則把最后一個 "->" 變成 "->>" 即可府瞄。

當(dāng)然,路徑的表示碘箍,也可以通過 #> '{label,names,1}' 的形式表示:

SELECT '{"label":{"names":["Amy","Kala","Lily"]},"color":"red","count":3}'::json 
       #> '{label,names,1}';

2.遍歷屬性

使用 json_each 函數(shù)遵馆,即可返回屬性鍵值對的數(shù)據(jù)集,數(shù)據(jù)集包括兩列丰榴,key 表示屬性货邓,value 表示屬性值。如下語句輸出所有結(jié)果:

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

3.遍歷數(shù)組

通過使用 json_array_length 函數(shù)獲取數(shù)組的長度四濒,然后根據(jù)索引遍歷整個數(shù)組即可换况。

DO $$
DECLARE 
    lv_row record; 
    lv_size int;
    jsonObj json := '{"label":{"names":["Amy","Kala","Lily"]},"color":"red","count":3}'::json;
BEGIN
    -- 取得label 下names 這個json數(shù)組
    jsonObj := jsonObj #> '{label,names}';
    -- 取得數(shù)組的長度
    lv_size := json_array_length(jsonObj);
    -- 按索引遍歷整個數(shù)組
    FOR i IN 0..lv_size-1 LOOP
        raise notice '%', jsonObj -> i;
    END LOOP;
END $$;

輸出:

NOTICE: "Amy"

NOTICE: "Kala"

NOTICE: "Lily"

4.創(chuàng)建一個 JSON

使用 json_build_object 函數(shù),傳遞 key, value 成對的參數(shù)即可創(chuàng)建一個 json盗蟆,如以下語句形成一個 api 常用的返回執(zhí)行情況的 json戈二。

SELECT json_build_object('code', 200, 'err_msg', 'run success!');

應(yīng)用示例

我們以填寫學(xué)生地址為例,傳遞給存儲過程的是一個 json 數(shù)組喳资,每個數(shù)組中的 json 對象包括了學(xué)生標(biāo)識與地址信息觉吭。

以下語句創(chuàng)建數(shù)據(jù)表

-- student_id 學(xué)生標(biāo)識, address 地址
CREATE TABLE student_address (student_id varchar(10) PRIMARY KEY, address varchar(100));
image.gif

以下為處理過程

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   
    -- 取得數(shù)組的長度
    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';

        -- 插入學(xué)生地址信息,如果存在則更新地址
        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 
$$

我們執(zhí)行以下操作

SELECT save_student_addresses_json(
    '[
     {"student_id":"01","address":"街道A"},
     {"student_id":"02","address":"街道B"}
    ]'
);

運行結(jié)果:{"err_code":200,"err_msg":"保存或更新 2 條記錄"}

小結(jié)一下

PostgreSQL 對 JSON 的操作支持特性很豐富仆邓,但文檔中那么多函數(shù)一下映入眼簾鲜滩,讓人覺得復(fù)雜凌亂伴鳖。本文從簡單易理解的幾個應(yīng)用場景出發(fā),希望能先爽上一把绒北,而后再細細深入黎侈。I love PostgreSQL!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市闷游,隨后出現(xiàn)的幾起案子峻汉,更是在濱河造成了極大的恐慌,老刑警劉巖脐往,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件休吠,死亡現(xiàn)場離奇詭異,居然都是意外死亡业簿,警方通過查閱死者的電腦和手機瘤礁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梅尤,“玉大人柜思,你說我怎么就攤上這事∠镌铮” “怎么了赡盘?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缰揪。 經(jīng)常有香客問我陨享,道長,這世上最難降的妖魔是什么钝腺? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任抛姑,我火速辦了婚禮,結(jié)果婚禮上艳狐,老公的妹妹穿的比我還像新娘定硝。我一直安慰自己,他們只是感情好毫目,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布喷斋。 她就那樣靜靜地躺著,像睡著了一般蒜茴。 火紅的嫁衣襯著肌膚如雪星爪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天粉私,我揣著相機與錄音顽腾,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛抄肖,可吹牛的內(nèi)容都是我干的久信。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼漓摩,長吁一口氣:“原來是場噩夢啊……” “哼裙士!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起管毙,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤腿椎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后夭咬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體啃炸,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年卓舵,在試婚紗的時候發(fā)現(xiàn)自己被綠了南用。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡掏湾,死狀恐怖裹虫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情融击,我是刑警寧澤筑公,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站砚嘴,受9級特大地震影響十酣,放射性物質(zhì)發(fā)生泄漏涩拙。R本人自食惡果不足惜际长,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望兴泥。 院中可真熱鬧工育,春花似錦、人聲如沸搓彻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旭贬。三九已至怔接,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間稀轨,已是汗流浹背扼脐。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瓦侮。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓艰赞,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肚吏。 傳聞我的和親對象是個殘疾皇子方妖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354