背景
工作中遇到一個需求,同時調(diào)用了兩個異步接口,這兩個接口在一段時間之后會回調(diào)預(yù)設(shè)的地址,將結(jié)果返回,在回調(diào)都完成后進行下一步操作。兩個接口之間沒有依賴關(guān)系,有可能同時返回。
我的設(shè)計非常簡單:
- 狀態(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)完成 - 返回的結(jié)果是Json結(jié)構(gòu)保存在表中的,這是由于該表上的數(shù)據(jù)類型太多(3種不同的type共享表中其他列的數(shù)據(jù)),使用Json區(qū)分數(shù)據(jù)用途。其實當(dāng)時已經(jīng)考慮到了回調(diào)同時到達的情況,但沒想好處理方法,因為我以為這版需求不是我做,于是埋了坑也沒管,血的教訓(xùn)
- 初步設(shè)計時認為將Json全量讀出再將修改后的Json結(jié)構(gòu)全量更新即可,很明顯,這存在并發(fā)問題,于是犯難了——文本結(jié)構(gòu)不像數(shù)字型,局部修改非常困難
- 如果改為串行調(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
類型有一些特點:
- 在寫入時校驗是否符合Json格式,格式不符合會報錯
- 沒有默認值
說實話,雖然了解到了這些特點,除了在寫入時有格式保障以外呢?還有什么嗎?
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)了局部修改。