MySQL支持JSON數據類型。相比於Json格式的字符串類型,JSON數據類型的優勢有:
- 存儲在JSON列中的JSON文檔的會被自動驗證。無效的文檔會產生錯誤;
- 最佳存儲格式。存儲在JSON列中的JSON文檔會被轉換為允許快速讀取文檔元素的內部格式。
存儲在JSON列中的任何JSON文檔的大小都受系統變量max_allowed_packet
的值的限制,可以使用JSON_STORAGE_SIZE()
函數獲得存儲JSON文檔所需的空間。
JSON值的局部更新
在MySQL8.0中,優化器可以執行JSON列的局部就地更新,而不用刪除舊文檔再將整個新文檔寫入該列。局部更新的條件:
- 正在更新的列被聲明為JSON;
- 該UPDATE語句使用任一的三個函數
JSON_SET()
,JSON_REPLACE()
或JSON_REMOVE()
更新列; - 輸入列和目標列必須是同一列;
- 所有更改都使用新值替換現有數組或對象值,並且不向父對象或數組添加任何新元素;
- 新值不能大於舊值;
創建JSON值
JSON數組包含在 字符[
和]
字符中,其中為一個由逗號分隔的值列表:
["abc", 10, null, true, false]
JSON對象包含在字符{
和}
字符中,其中為一組由逗號分隔的鍵值對,鍵必須是字符串:
{"k1": "value", "k2": 10}
在JSON數組和JSON對象的值中允許嵌套:
[99, {"id": "HK500", "cost": 75.99}, ["hot", "cold"]] {"k1": "value", "k2": [10, 20]}
下例中向創建一個只有一個JSON列的表格t_json
,並向其中添加JSON值:
mysql> CREATE TABLE t_json (jdoc JSON) ENGINE=InnoDB DEFAULT CHARSET=utf8; Query OK, 0 rows affected, 1 warning (0.73 sec) mysql> INSERT INTO t_json VALUES('[1,2]'); Query OK, 1 row affected (0.17 sec mysql> INSERT INTO t_json VALUES('{"key1":"value1","key2":"value2"}'); Query OK, 1 row affected (0.27 sec) mysql> INSERT INTO t_json VALUES('"HELLO"'); Query OK, 1 row affected (0.20 sec)
若添加的值為非JSON格式,則報錯:
mysql> INSERT INTO t_json VALUES("HELLO"); ERROR 3140 (22032): Invalid JSON text: "Invalid value." at position 0 in value for column 't_json.jdoc'.
查看t_json
:
mysql> SELECT * FROM t_json; +--------------------------------------+ | jdoc | +--------------------------------------+ | [1, 2] | | {"key1": "value1", "key2": "value2"} | | "HELLO" | +--------------------------------------+ 3 rows in set (0.00 sec)
JSON_TYPE()
函數嘗試將傳入的值其解析為JSON值。如果值有效,則返回值的JSON類型,否則產生錯誤:
mysql> SELECT JSON_TYPE('["a","b",true,13]'); +--------------------------------+ | JSON_TYPE('["a","b",true,13]') | +--------------------------------+ | ARRAY | +--------------------------------+ 1 row in set (0.04 sec) mysql> SELECT JSON_TYPE('[a,"b",true,13]'); //注意 a ERROR 3141 (22032): Invalid JSON text in argument 1 to function json_type: "Invalid value." at position 1.
JSON_ARRAY()
接收傳入的值列表(可以為空),返回包含這些值的JSON數組:
mysql> SELECT JSON_ARRAY('ab',false,13); +---------------------------+ | JSON_ARRAY('ab',false,13) | +---------------------------+ | ["ab", false, 13] | +---------------------------+ 1 row in set (0.00 sec) mysql> SELECT JSON_ARRAY(); +--------------+ | JSON_ARRAY() | +--------------+ | [] | +--------------+ 1 row in set (0.00 sec)
JSON_OBJECT()
接收傳入的鍵值對列表(可以為空),並返回包含這些鍵值對的JSON對象:
mysql> SELECT JSON_OBJECT('key1','a','key2','b'); +------------------------------------+ | JSON_OBJECT('key1','a','key2','b') | +------------------------------------+ | {"key1": "a", "key2": "b"} | +------------------------------------+ 1 row in set (0.03 sec)
如果傳入的參數不能組成鍵值對,則報錯:
mysql> SELECT JSON_OBJECT('key1','value1','key2'); ERROR 1582 (42000): Incorrect parameter count in the call to native function 'JSON_OBJECT'
JSON_MERGE_PRESERVE()
獲取兩個或多個JSON文檔並返回組合結果:
mysql> SELECT JSON_MERGE_PRESERVE('["a", 1]', '{"key": "value"}'); +-----------------------------------------------------+ | JSON_MERGE_PRESERVE('["a", 1]', '{"key": "value"}') | +-----------------------------------------------------+ | ["a", 1, {"key": "value"}] | +-----------------------------------------------------+ 1 row in set (0.03 sec)
因此我們也可以使用以上三種方法向表中添加JSON值,可以一定程度地避免輸入格式錯誤:
mysql> INSERT INTO t_json VALUES(JSON_ARRAY('json_array')); Query OK, 1 row affected (0.19 sec) mysql> INSERT INTO t_json VALUES(JSON_OBJECT('key','hello')); Query OK, 1 row affected (0.09 sec) mysql> INSERT INTO t_json VALUES(JSON_MERGE_PRESERVE(JSON_OBJECT('key','hello'),JSON_ARRAY(1,2))); Query OK, 1 row affected (0.14 sec) mysql> SELECT * FROM t_json; +--------------------------------------+ | jdoc | +--------------------------------------+ | [1, 2] | | {"key1": "value1", "key2": "value2"} | | "HELLO" | | ["json_array"] | | {"key": "hello"} | | [{"key": "hello"}, 1, 2] | +--------------------------------------+ 6 rows in set (0.00 sec)
JSON值的規范化,合並和自動包裝
解析字符串並發現字符串是有效的JSON文檔時,它在被解析時也會被規范化。對於重復的鍵(key
),后面的值(value
)會覆蓋前面的值。如下:
mysql> SELECT JSON_OBJECT('x',1,'y',2,'x','a','x','b'); +------------------------------------------+ | JSON_OBJECT('x',1,'y',2,'x','a','x','b') | +------------------------------------------+ | {"x": "b", "y": 2} | +------------------------------------------+ 1 row in set (0.07 sec)
這種“覆蓋”在向JSON列添加值時也會發生。
在MySQL8.0.3之前的版本中,與此相反,對於被重復的鍵,它的第一個值會被保留,后添加的值則會被拋棄。
合並JSON值
MySQL8.0.3及更高版本中,有兩種合並函數:JSON_MERGE_PRESERVE()
和 JSON_MERGE_PATCH()
。下面具討論它們的區別。
- 合並數組:
mysql> SELECT JSON_MERGE_PATCH('[1, 2]', '["a", "b", "c"]','[1, 2]', '[true, false]'); +-------------------------------------------------------------------------+ | JSON_MERGE_PATCH('[1, 2]', '["a", "b", "c"]','[1, 2]', '[true, false]') | +-------------------------------------------------------------------------+ | [true, false] | +-------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT JSON_MERGE_PRESERVE('[1, 2]', '["a", "b", "c"]','[1, 2]', '[true, false]'); +----------------------------------------------------------------------------+ | JSON_MERGE_PRESERVE('[1, 2]', '["a", "b", "c"]','[1, 2]', '[true, false]') | +----------------------------------------------------------------------------+ | [1, 2, "a", "b", "c", 1, 2, true, false] | +----------------------------------------------------------------------------+ 1 row in set (0.00 sec)
合並數組時,JSON_MERGE_PRESERVE
只保留最后傳入的數組參數,而JSON_MERGE_PRESERVE
則按傳入順序將數組參數連接。
- 合並對象
mysql> SELECT JSON_MERGE_PATCH('{"a": 3, "b": 2}', '{"c": 3, "a": 4}', '{"c": 5, "d": 3}'); +------------------------------------------------------------------------------+ | JSON_MERGE_PATCH('{"a": 3, "b": 2}', '{"c": 3, "a": 4}', '{"c": 5, "d": 3}') | +------------------------------------------------------------------------------+ | {"a": 4, "b": 2, "c": 5, "d": 3} | +------------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT JSON_MERGE_PRESERVE('{"a": 3, "b": 2}', '{"c": 3, "a": 4}', '{"c": 5, "d": 3}'); +---------------------------------------------------------------------------------+ | JSON_MERGE_PRESERVE('{"a": 3, "b": 2}', '{"c": 3, "a": 4}', '{"c": 5, "d": 3}') | +---------------------------------------------------------------------------------+ | {"a": [3, 4], "b": 2, "c": [3, 5], "d": 3} | +---------------------------------------------------------------------------------+ 1 row in set (0.00 sec)
合並對象時,對於重復鍵,JSON_MERGE_PRESERVE
只保留最后傳入的鍵值,而JSON_MERGE_PRESERVE
重復鍵的所有值保留為數組。
搜索和修改JSON值
在了解搜索和修改JSON值之前,先來看看JSON的路徑語法。
路徑語法
.keyName
:JSON對象中鍵名為keyName
的值;- 對於不合法的鍵名(如有空格),在路徑引用中必須用雙引號
"
將鍵名括起來,例,."key name"
; [index]
:JSON數組中索引為index
的值,JSON數組的索引同樣從0開始;[index1 to index2]
:JSON數組中從index1
到index2
的值的集合;.*
: JSON對象中的所有value
;[*]
: JSON數組中的所有值;prefix**suffix
: 以prefix
開頭並以suffix
結尾的路徑;**.keyName
為多個路徑,如對於JSON對象'{"a": {"b": 1}, "c": {"b": 2}}'
,'$**.b'
指路徑$.a.b
和$.c.b
;- 不存在的路徑返回結果為NULL;
- 前導$字符表示當前正在使用的JSON文檔
- 例子:對於數組
[3, {"a": [5, 6], "b": 10}, [99, 100]]
$[1]
為{"a": [5, 6], "b": 10}
。[1].a
為[5, 6]
。$[1].a[1]
為6
。$[1].b
為10
。$[2][0]
為99
。
搜索
JSON_EXTRACT
提取JSON值,直接看例子:
- JSON對象
mysql> SELECT JSON_EXTRACT('{"id": 29, "name": "Taylor"}', '$.name'); +--------------------------------------------------------+ | JSON_EXTRACT('{"id": 29, "name": "Taylor"}', '$.name') | +--------------------------------------------------------+ | "Taylor" | +--------------------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT JSON_EXTRACT('{"id": 29, "name": "Taylor"}', '$.*'); +-----------------------------------------------------+ | JSON_EXTRACT('{"id": 29, "name": "Taylor"}', '$.*') | +-----------------------------------------------------+ | [29, "Taylor"] | +-----------------------------------------------------+ 1 row in set (0.00 sec)
嵌套查詢:
SELECT JSON_EXTRACT(JSON_EXTRACT('{"char1": {"total": 4, "consumed": 1}, "char2": {"total": 8, "consumed": 1}}', '$.char1'), '$.total');
4
- JSON數組
mysql> SELECT JSON_EXTRACT('["a", "b", "c"]', '$[1]'); +-----------------------------------------+ | JSON_EXTRACT('["a", "b", "c"]', '$[1]') | +-----------------------------------------+ | "b" | +-----------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT JSON_EXTRACT('["a", "b", "c"]', '$[1 to 2]'); +----------------------------------------------+ | JSON_EXTRACT('["a", "b", "c"]', '$[1 to 2]') | +----------------------------------------------+ | ["b", "c"] | +----------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT JSON_EXTRACT('["a", "b", "c"]', '$[*]'); +-----------------------------------------+ | JSON_EXTRACT('["a", "b", "c"]', '$[*]') | +-----------------------------------------+ | ["a", "b", "c"] | +-----------------------------------------+ 1 row in set (0.00 sec)
修改
JSON_REPLACE
替換值(只替換已經存在的舊值)JSON_SET
設置值(替換舊值,並插入不存在的新值)JSON_INSERT
插入值(插入新值,但不替換已經存在的舊值)JSON_REMOVE
刪除JSON數據,刪除指定值后的JSON文檔
JSON_REPLACE
與JSON_SET
的區別:
// 舊值存在 mysql> SELECT JSON_REPLACE('{"id": 29, "name": "Taylor"}', '$.name', 'Mere'); +----------------------------------------------------------------+ | JSON_REPLACE('{"id": 29, "name": "Taylor"}', '$.name', 'Mere') | +----------------------------------------------------------------+ | {"id": 29, "name": "Mere"} | +----------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT JSON_SET('{"id": 29, "name": "Taylor"}', '$.name', "Mere"); +------------------------------------------------------------+ | JSON_SET('{"id": 29, "name": "Taylor"}', '$.name', 'Mere') | +------------------------------------------------------------+ | {"id": 29, "name": "Mere"} | +------------------------------------------------------------+ 1 row in set (0.00 sec) // 舊值不存在 mysql> SELECT JSON_REPLACE('{"id": 29, "name": "Taylor"}', '$.cat', 'Mere'); +---------------------------------------------------------------+ | JSON_REPLACE('{"id": 29, "name": "Taylor"}', '$.cat', 'Mere') | +---------------------------------------------------------------+ | {"id": 29, "name": "Taylor"} | +---------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT JSON_SET('{"id": 29, "name": "Taylor"}', '$.cat', 'Mere'); +-----------------------------------------------------------+ | JSON_SET('{"id": 29, "name": "Taylor"}', '$.cat', 'Mere') | +-----------------------------------------------------------+ | {"id": 29, "cat": "Mere", "name": "Taylor"} | +-----------------------------------------------------------+ 1 row in set (0.00 sec)
JSON_INSERT
和JSON_SET
:
// 舊值存在 mysql> SELECT JSON_INSERT('[1, 2, 3]', '$[1]', 4); +-------------------------------------+ | JSON_INSERT('[1, 2, 3]', '$[1]', 4) | +-------------------------------------+ | [1, 2, 3] | +-------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT JSON_SET('[1, 2, 3]', '$[1]', 4); +----------------------------------+ | JSON_SET('[1, 2, 3]', '$[1]', 4) | +----------------------------------+ | [1, 4, 3] | +----------------------------------+ 1 row in set (0.00 sec) //舊值不存在 mysql> SELECT JSON_INSERT('[1, 2, 3]', '$[4]', 4); +-------------------------------------+ | JSON_INSERT('[1, 2, 3]', '$[4]', 4) | +-------------------------------------+ | [1, 2, 3, 4] | +-------------------------------------+ 1 row in set (0.00 sec) mysql> SELECT JSON_SET('[1, 2, 3]', '$[4]', 4); +----------------------------------+ | JSON_SET('[1, 2, 3]', '$[4]', 4) | +----------------------------------+ | [1, 2, 3, 4] | +----------------------------------+ 1 row in set (0.00 sec)
JSON_REMOVE
:
mysql> SELECT JSON_REMOVE('[1, 2, 3]', '$[1]'); +----------------------------------+ | JSON_REMOVE('[1, 2, 3]', '$[1]') | +----------------------------------+ | [1, 3] | +----------------------------------+ 1 row in set (0.00 sec) mysql> SELECT JSON_REMOVE('[1, 2, 3]', '$[4]'); +----------------------------------+ | JSON_REMOVE('[1, 2, 3]', '$[4]') | +----------------------------------+ | [1, 2, 3] | +----------------------------------+ 1 row in set (0.00 sec) mysql> SELECT JSON_REMOVE('{"id": 29, "name": "Taylor"}', '$.name'); +-------------------------------------------------------+ | JSON_REMOVE('{"id": 29, "name": "Taylor"}', '$.name') | +-------------------------------------------------------+ | {"id": 29} | +-------------------------------------------------------+ 1 row in set (0.00 sec)
JSON值的比較和排序
可以使用=
,<
,<=
,>
,>=
,<>
,!=
,和 <=>
對JSON值進行比較。
JSON值的比較先比較值的類型。如果類型不同,則直接 返回類型的優先級的比較結果;如果類型相同,再進行值的內容的比較。
- JSON中值的類型的優先級從高到低為:
BLOB BIT OPAQUE DATETIME TIME DATE BOOLEAN ARRAY OBJECT STRING INTEGER, DOUBLE NULL
OPAQUE
值是不屬於其他類型的值。
- JSON值的內容的比較規則(因類型不同而有差別):
- BLOB
比較兩個值的前N個字節,其中N為較短的值的字節數。如果前N個字節相同,則較短的值較小。BIT和OPAQUE與BLOB的規則相同。 - DATETIME
較早時間點的值較小。如果兩個值分別為 MySQL DATETIME and TIMESTAMP類型且表示的是相同的時間點,則這兩個值相等。 - TIME
較少的是時間值較小。 - DATE
較早的日期值較小。 - ARRAY
較短的數組較小。
如果兩個數組長度相同,且相同索引處的值相同,則兩個數組相等。
對於不行等的數組,它們的大小順序由兩數組中第一個不同的元素決定。
例子:
- BLOB
[] < ["a"] < ["ab"] < ["ab", "cd", "ef"] < ["ab", "ef"]
- BOOLEAN
false
<true
- OBJECT
具有完全相同的鍵值對的兩對象相等。如
{"a": 1, "b": 2} = {"b": 2, "a": 1}
- STRING
與BLOB比較規則相似。區分大小寫。
如:
"A"<"a" < "ab" < "b" < "bc"
- 如果進行INTEGER列和DOUBLE列的比較,則integer數會被轉為double數,即精確值轉為近似值,再進行比較;
- 如果查詢比較包含數字的兩個JSON列,則無法預先知道數字是INTEGER還是DOUBLE,比較時會將近似值轉為精確值,在進行比較。
- INTEGER比較
9223372036854775805 < 9223372036854775806 < 9223372036854775807
- DOUBLE比較
9223372036854775805 = 9223372036854775806 = 9223372036854775807 = 9.223372036854776e18
- 任何JSON值與NULL比較的結果為
UNKOWN
- JSON值與非JSON值比較時,非JSON值會被轉為JSON值
JSON值和非JSON值轉換
轉換規則為:
other type | CAST(other type AS JSON) | CAST(JSON AS other type) |
---|---|---|
JSON | 沒變化 | 沒變化 |
utf8 字符類型(utf8mb4 ,utf8 ,ascii ) |
字符串被解析為JSON值 | JSON值被序列化為utf8mb4 字符串 |
其他字符類型 | 其他字符編碼被隱式轉換為utf8mb4 ,並按utf8 字符類型進行處理 |
JSON值被序列化為utf8mb4 字符串,然后再被轉換為其他字符編碼。結果可能沒有意義。 |
NULL | 結果為JSON類型的NULL值 | 不適用 |
Geometry類型 | ST_AsGeoJSON() 將Geometry值轉換為JSON文檔 |
非法操作。解決辦法: 將CAST(JSON AS other type) 的結果傳遞給CHAR)ST_GeomFromGeoJSON() |
所有其他類型 | 轉換結果是由單個標量值組成的JSON文檔 | 如果JSON文檔由目標類型的單個標量值組成,並且標量值可以強制轉換為目標類型,則成功轉換。否則,返回NULL 並發出警告。 |