省時省力的MySql 5.7 Json數(shù)據(jù)類型及操作

背景

工作中遇到一個需求,同時調(diào)用了兩個異步接口,這兩個接口在一段時間之后會回調(diào)預(yù)設(shè)的地址,將結(jié)果返回,在回調(diào)都完成后進行下一步操作。兩個接口之間沒有依賴關(guān)系,有可能同時返回。

我的設(shè)計非常簡單:

  1. 狀態(tài)值按位表示,如右數(shù)第一位為1說明在等待異步接口A,第二位為1說明在等待異步接口B。即01表示等待A回調(diào),10等待B,11同時等待A和B,這樣在接口回調(diào)時只需要在數(shù)據(jù)庫使用位操作即可確認回調(diào)已完成,如status = status ^ 2表示B接口回調(diào)完成
  2. 返回的結(jié)果是Json結(jié)構(gòu)保存在表中的,這是由于該表上的數(shù)據(jù)類型太多(3種不同的type共享表中其他列的數(shù)據(jù)),使用Json區(qū)分數(shù)據(jù)用途。其實當(dāng)時已經(jīng)考慮到了回調(diào)同時到達的情況,但沒想好處理方法,因為我以為這版需求不是我做,于是埋了坑也沒管,血的教訓(xùn)
  3. 初步設(shè)計時認為將Json全量讀出再將修改后的Json結(jié)構(gòu)全量更新即可,很明顯,這存在并發(fā)問題,于是犯難了——文本結(jié)構(gòu)不像數(shù)字型,局部修改非常困難
  4. 如果改為串行調(diào)用兩個接口,實現(xiàn)起來會非常麻煩和啰嗦,這是最后手段

今天看到數(shù)據(jù)導(dǎo)出工單里有一條奇怪的SQL語句:

SELECT json_extract(detail,"$.width") ... FROM ... WHERE ...

第一眼并沒有很在意,但是鬼使神差我又回過頭來看了一下這條記錄,從語義上看似乎在單獨讀取Json結(jié)構(gòu)中的數(shù)據(jù),但是真的有這種操作嗎?還是說這是自定義函數(shù)?這會不會是解決我燃眉之急的曙光?帶著將信將疑的態(tài)度打開Google,沒想到就此打開了新世界的大門。

MySql中的Json類型是什么

在上一家公司工作時,保存JSON數(shù)據(jù)的列我們都習(xí)慣使用text類型,要不然就使用varchar類型,所有對Json的操作都是上述讀出-修改-更新的過程。

而在新公司的表結(jié)構(gòu)中,出現(xiàn)了從來沒有見過的數(shù)據(jù)類型——json類型。

json類型有一些特點:

  1. 在寫入時校驗是否符合Json格式,格式不符合會報錯
  2. 沒有默認值

說實話,雖然了解到了這些特點,除了在寫入時有格式保障以外呢?還有什么嗎?

MySql 5.7 的內(nèi)置Json操作函數(shù)

是的,這就是Json結(jié)構(gòu)的最大特點,可以像在其他高級編程語言中一樣,將Json作為Json操作,而不再是當(dāng)作字符串類型操作。

想象一下,如果對Json數(shù)據(jù)的局部修改從讀取-反序列化-修改-序列化-回寫變?yōu)?code>直接修改,能節(jié)省多少資源;
以前我們讀取局部數(shù)據(jù)時必須經(jīng)歷讀取-反序列化-根據(jù)Key查找,而現(xiàn)在我們可以直接根據(jù)Key讀取
以前我們刷庫時遇到Json結(jié)構(gòu),那簡直是噩夢,而現(xiàn)在甚至可以一個語句完成;
以前我們根據(jù)Json的局部數(shù)據(jù)作為SQL的WHERE條件,那根本是件不可能的事,而Json函數(shù)就可以......

沒錯,MySql的內(nèi)置Json函數(shù)很強大,解決了眾多曾經(jīng)不敢想的問題。

非常實用的Json函數(shù)

JSON_EXTRACT 查詢局部數(shù)據(jù)

mysql> SELECT JSON_EXTRACT('{"name":"Zhaim","tel":"13240133388"}',"$.name");
+---------------------------------------------------------------+
| JSON_EXTRACT('{"name":"Zhaim","tel":"13240133388"}',"$.name") |
+---------------------------------------------------------------+
| "Zhaim"                                                       |
+---------------------------------------------------------------+

# 可以使用 column->path 的形式提取元素的值,對應(yīng)字符串類型的 category->'$.name' 中還包含著雙引號,可以用 JSON_UNQUOTE 函數(shù)將雙引號去掉
# 從 MySQL 5.7.13 起也可以通過這個操作符 ->> 這個和 JSON_UNQUOTE 是等價的
# 用 -> 直接提取時要注意元素值的類型
mysql> SELECT * FROM lnmp WHERE category->>'$.name' = 'lnmp.cn';
+----+------------------------------+-----------+
| id | category                     | tags      |
+----+------------------------------+-----------+
|  1 | {"id": 1, "name": "lnmp.cn"} | [1, 2, 3] |
+----+------------------------------+-----------+

JSON_INSERT 插入新的元素

mysql> UPDATE lnmp SET category = JSON_INSERT(category, '$.name', 'lnmp', '$.url', 'www.lnmp.cn') WHERE id = 1;

mysql> SELECT * FROM lnmp;
+----+----------------------------------------------------+-----------+
| id | category                                           | tags      |
+----+----------------------------------------------------+-----------+
|  1 | {"id": 1, "url": "www.lnmp.cn", "name": "lnmp.cn"} | [1, 3, 4] |
|  2 | {"id": 2, "name": "php.net"}                       | [1, 3, 5] |
+----+----------------------------------------------------+-----------+

JSON_SET 插入或覆蓋元素

mysql> UPDATE lnmp SET category = JSON_SET(category, '$.host', 'www.lnmp.cn', '$.url', 'http://www.lnmp.cn') WHERE id = 1;

mysql> SELECT * FROM lnmp;
+----+----------------------------------------------------------------------------------+-----------+
| id | category                                                                         | tags      |
+----+----------------------------------------------------------------------------------+-----------+
|  1 | {"id": 1, "url": "http://www.lnmp.cn", "host": "www.lnmp.cn", "name": "lnmp.cn"} | [1, 3, 4] |
|  2 | {"id": 2, "name": "php.net"}                                                     | [1, 3, 5] |
+----+----------------------------------------------------------------------------------+-----------+

JSON_REPLACE 替換已有元素

mysql> UPDATE lnmp SET category = JSON_REPLACE(category, '$.name', 'php', '$.url', 'http://www.php.net') WHERE id = 2;

mysql> SELECT * FROM lnmp;
+----+----------------------------------------------------------------------------------+-----------+
| id | category                                                                         | tags      |
+----+----------------------------------------------------------------------------------+-----------+
|  1 | {"id": 1, "url": "http://www.lnmp.cn", "host": "www.lnmp.cn", "name": "lnmp.cn"} | [1, 3, 4] |
|  2 | {"id": 2, "name": "php"}                                                         | [1, 3, 5] |
+----+----------------------------------------------------------------------------------+-----------+

JSON_REMOVE 刪除部分元素

mysql> UPDATE lnmp SET category = JSON_REMOVE(category, '$.url', '$.host') WHERE id = 1;

mysql> SELECT * FROM lnmp;
+----+------------------------------+-----------+
| id | category                     | tags      |
+----+------------------------------+-----------+
|  1 | {"id": 1, "name": "lnmp.cn"} | [1, 3, 4] |
|  2 | {"id": 2, "name": "php"}     | [1, 3, 5] |
+----+------------------------------+-----------+

Json函數(shù)列表

Name Description
JSON_APPEND() Append data to JSON document
JSON_ARRAY() Create JSON array
JSON_ARRAY_APPEND() Append data to JSON document
JSON_ARRAY_INSERT() Insert into JSON array-> Return value from JSON column after evaluating path; equivalent to JSON_EXTRACT().
JSON_CONTAINS() Whether JSON document contains specific object at path
JSON_CONTAINS_PATH() Whether JSON document contains any data at path
JSON_DEPTH() Maximum depth of JSON document
JSON_EXTRACT() Return data from JSON document->> Return value from JSON column after evaluating path and unquoting the result; equivalent to JSON_UNQUOTE(JSON_EXTRACT()).
JSON_INSERT() Insert data into JSON document
JSON_KEYS() Array of keys from JSON document
JSON_LENGTH() Number of elements in JSON document
JSON_MERGE() Merge JSON documents, preserving duplicate keys. Deprecated synonym for JSON_MERGE_PRESERVE()
JSON_MERGE_PRESERVE() Merge JSON documents, preserving duplicate keys
JSON_OBJECT() Create JSON object
JSON_QUOTE() Quote JSON document
JSON_REMOVE() Remove data from JSON document
JSON_REPLACE() Replace values in JSON document
JSON_SEARCH() Path to value within JSON document
JSON_SET() Insert data into JSON document
JSON_TYPE() Type of JSON value
JSON_UNQUOTE() Unquote JSON value
JSON_VALID() Whether JSON value is valid

ps. 注意在MySql 5.7.8及以上才可以使用,如果存儲結(jié)構(gòu)可以選擇json類型,就肯定是可以用的

總結(jié)

通過JSON_SET()方法,實現(xiàn)了類似status = status ^ 2這樣的語句,最終實現(xiàn)了局部修改。

參考資料

MySQL 5.7 新特性 JSON 的創(chuàng)建,插入,查詢,更新

MySQL JSON數(shù)據(jù)類型操作 - 掘金

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 概述 mysql自5.7.8版本開始,就支持了json結(jié)構(gòu)的數(shù)據(jù)存儲和查詢,這表明了mysql也在不斷的學(xué)習(xí)和增加...
    SimonChen閱讀 1,525評論 0 1
  • 示例表 后面的所有的表demo_json結(jié)構(gòu)都是這個 json_set 用于將對應(yīng)的json已有的字段進行修改 語...
    老柿子閱讀 759評論 0 0
  • 軟件行業(yè)唯一不變的就是變化,比如功能上線之后,客戶或 PM 需要對已有的功能增加一些合理的需求,完成這些工作必須通...
    梅西愛騎車閱讀 1,841評論 0 1
  • SQL中的JSON數(shù)據(jù)類型 概述 MySQL支持原生JSON類型,使用JSON數(shù)據(jù)類型相較于將JSON格式的字符串...
    FoxLayla閱讀 15,042評論 1 6
  • 久違的晴天,家長會。 家長大會開好到教室時,離放學(xué)已經(jīng)沒多少時間了。班主任說已經(jīng)安排了三個家長分享經(jīng)驗。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,550評論 16 22