MySQL JSON類型


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數組中從index1index2的值的集合;
  • .*: 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].b10
    • $[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_REPLACEJSON_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_INSERTJSON_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
      較短的數組較小。
      如果兩個數組長度相同,且相同索引處的值相同,則兩個數組相等。
      對於不行等的數組,它們的大小順序由兩數組中第一個不同的元素決定。
      例子:
[] < ["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列的比較,則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 並發出警告。
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM