MySQL5.7之Json Column和Generated Column

MySQL5.7新增兩種字段類型:Json和Generated,Generated型的產生和Json的關係密不可分,如果沒有Generated類型,Json類型在強大,生產中可能也無法使用,因爲Json不支持索引,但是如果要查詢Json裏的數據,沒有索引就是全表掃描,在執行效率上肯定是不能用於生產環境的,但是有了Generated類型就不同了,Generated類型簡單地說是一個虛擬字段,值是不可更新的,值來源其他字段或者字段間計算或是轉化而來的,這種類型是可以創建索引,利用Generated的特性,就可以間接的給Json類型中的key創建索引,解決Json不能創建索引的問題。簡而言之, Generated類型的產生,爲Json類型在索引方面的問題提供了支持。JSON的值包含單個值、數組、元組、標註的Json格式等幾種格式。

客觀地說,Json類型的出現,爲研發提供了便利,提供了類似NoSQL的支持,但是走DBA運維的角度來看,也許是一個麻煩,這個麻煩和text等大字段的麻煩是一致的,json可能在實際的生產中會成爲text字段的變種,望DBA在以後5.7的使用中密切關注這個類型。

一、創建帶有JSON字段的表

1、直接創建一張測試帶JSON字段的表

CREATE TABLE json_test (

id int(11) NOT NULL AUTO_INCREMENT,

group_name varchar(20) DEFAULT NULL,

user_info json DEFAULT NULL,

PRIMARY KEY (id)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2、在表中添加一個JSON類型的字段

ALTER TABLE json_test ADD COLUMN user_info JSON DEFAULT NULL;

3、插入數據

p_w_picpath_thumb2

二、JSON字段相關查詢

1、查詢全部結果

p_w_picpath_thumb4

2:JSON_EXTRACT查詢

JSON_EXTRACT這個函數主要用作精確匹配,如查找user_info中age=21的記錄或者查詢name=’wangwei’的記錄,或者name like ‘%wangwei%’模糊查詢:

SELECT * FROM json_test WHERE JSON_EXTRACT(user_info,'$.age')=21;

p_w_picpath_thumb6

p_w_picpath_thumb8

在值爲數組的時候,數組的起始位置值爲0,同時,MySQL在5.5.9的版本增加了->,提供了等價於JSON_EXTRACT另外一種寫:

SELECT * FROM json_test WHERE user_info->"$.age"=21;

p_w_picpath_thumb10

p_w_picpath_thumb12

3、JSON_SEARCH查詢

JSON_SEARCH字符串查找函數,查找json所有key中值爲字符串’wangwei’的記錄,爲精確匹配;加上%的話有點like的意思:

SELECT * FROM json_test WHERE JSON_SEARCH(user_info,'all','wangwei') IS NOT NULL;

SELECT * FROM json_test WHERE JSON_SEARCH(user_info,'all','wangwei%') IS NOT NULL;

SELECT * FROM json_test WHERE JSON_SEARCH(user_info,'all','%wangwei%') IS NOT NULL;

4、JSON_CONTAINS判斷key是否包含指定的值

JSON_CONTAINS判斷key是否包含指定的值,返回值爲0或1或者是null,對應的情況爲不包含、包含、和指定的key不存在集中情況:

p_w_picpath_thumb14


5、JSON_CONTAINS_PATH判斷key在字段中是否存在

JSON_CONTAINS_PATH判斷key在字段中是否存在,返回值爲0和1,格式爲JSON_CONTAINS_PATH(json_doc, one_or_all, path[, path] ...),如果爲one的時候,代表path中只要有一個在就爲1,反之爲0;如果爲all的話必須全部包含,返回爲1,反之爲0:

p_w_picpath_thumb16

6、JSON_LENGTH長度查詢

JSON_LENGTH是用來計算JSON字段的長度:

p_w_picpath_thumb18

7:JSON_DEPTH層級查詢

JSON_DEPTH這個主要是用來查詢Json字段的層級,空的爲1,所以正常情況下Json字段類型不爲空,這個值的查詢出的結果應該大於等於2:

p_w_picpath_thumb20

8:JSON_TYPE查詢Json字段中key值的數據類型

JSON_TYPE主要用於查詢Json字段類型中key值的數據類型,顯示的類型按照分類如下:

Purely JSON types:

OBJECT: JSON objects;ARRAY: JSON arrays;BOOLEAN: The JSON true and false literals;NULL: The JSON null literal

Numeric types:

INTEGER: MySQL TINYINT, SMALLINT, MEDIUMINT and INT and BIGINT scalars;DOUBLE: MySQL DOUBLE FLOAT scalars;DECIMAL: MySQL DECIMAL and NUMERIC scalars

Temporal types:

DATETIME: MySQL DATETIME and TIMESTAMP scalars;DATE: MySQL DATE scalars;TIME: MySQL TIME scalars

String types:

STRING: MySQL utf8 character type scalars;CHAR, VARCHAR, TEXT, ENUM, and SET

Binary types:

BLOB: MySQL binary type scalars;BINARY, VARBINARY, BLOB;BIT: MySQL BIT scalars

All other types:

OPAQUE (raw bits)

p_w_picpath_thumb22

9、JSON_UNQUOTE去掉前後特殊字符

JSON_UNQUOTE主要是用來去掉前後的特殊字符,格式化值,目前支持一下幾種字符:

clip_p_w_picpath0234_thumb1

p_w_picpath_thumb24

三、JSON字段相關寫操作

1JSON_ARRAY_APPEND數組類的追加

這個用法主要是處理JSON字段中值數組型的數據,主要是追加值,不是標準數組類型的,這個函數會自動轉化爲數組形式,把之前的值作爲數組的第一個元素然後進行追加,有批量追加方法:

mysql> SET @j = '["a", ["b", "c"], "d"]';
在字段中第二個數值的數組中追加一個元素1:
mysql> SELECT JSON_ARRAY_APPEND(@j, '$[1]', 1);
+----------------------------------+
| JSON_ARRAY_APPEND(@j, '$[1]', 1) |
+----------------------------------+
| ["a", ["b", "c", 1], "d"]         |
+----------------------------------+
在字段中第一個數值的數組中追加一個元素2,如果不是數組形式的單個值會改變其爲數組並追加:
mysql> SELECT JSON_ARRAY_APPEND(@j, '$[0]', 2);
+----------------------------------+
| JSON_ARRAY_APPEND(@j, '$[0]', 2) |
+----------------------------------+
| [["a", 2], ["b", "c"], "d"]      |
+----------------------------------+
在字段中第二個數值的第一個數組中追加一個元素3,如果不是數組形式的單個值會改變其爲數組並追加:
mysql> SELECT JSON_ARRAY_APPEND(@j, '$[1][0]', 3);
+-------------------------------------+
| JSON_ARRAY_APPEND(@j, '$[1][0]', 3) |
+-------------------------------------+
| ["a", [["b", 3], "c"], "d"]         |
+-------------------------------------+
在字段中key爲”b”的value數組中追加一個元素”x”:
mysql> SET @j = '{"a": 1, "b": [2, 3], "c": 4}';
mysql> SELECT JSON_ARRAY_APPEND(@j, '$.b', 'x');
+------------------------------------+
| JSON_ARRAY_APPEND(@j, '$.b', 'x')  |
+------------------------------------+
| {"a": 1, "b": [2, 3, "x"], "c": 4} |
+------------------------------------+
在字段中key爲”c”的value數組中追加一個元素”y”:
mysql> SELECT JSON_ARRAY_APPEND(@j, '$.c', 'y');
+--------------------------------------+
| JSON_ARRAY_APPEND(@j, '$.c', 'y')    |
+--------------------------------------+
| {"a": 1, "b": [2, 3], "c": [4, "y"]} |
+--------------------------------------+
如果JSON字段爲標準的JSON類型,使用這個函數則會把其變化數組形式,並追加值:
mysql> SET @j = '{"a": 1}';
mysql> SELECT JSON_ARRAY_APPEND(@j, '$', 'z');
+---------------------------------+
| JSON_ARRAY_APPEND(@j, '$', 'z') |
+---------------------------------+
| [{"a": 1}, "z"]                    |
+---------------------------------+
如果JSON字段爲標準的JSON類型,批量追加:
mysql> SELECT JSON_ARRAY_APPEND(@j, '$', 'z','$', 'w');
+---------------------------------+
| JSON_ARRAY_APPEND(@j, '$', 'z') |
+---------------------------------+
| [{"a": 1}, "z", "w"]              |
+---------------------------------+

2JSON_ARRAY_INSERT數組類的插入

這個用法主要是處理JSON字段中值數組型的數據,主要是插入,不是標準數組類型的,這個函數會自動轉化爲數組形式,把之前的值作爲數組的第一個元素然後進行追加:

mysql> SET @j = '["a", {"b": [1, 2]}, [3, 4]]';
在數組第1個值後面插入字符’x’ 元素:
mysql> SELECT JSON_ARRAY_INSERT(@j, '$[1]', 'x');
+------------------------------------+
| JSON_ARRAY_INSERT(@j, '$[1]', 'x') |
+------------------------------------+
| ["a", "x", {"b": [1, 2]}, [3, 4]]  |
在數組第1個值前面插入字符’x’ 元素:
mysql> SELECT JSON_ARRAY_INSERT(@j, '$[0]', 'x');
+------------------------------------+
| JSON_ARRAY_INSERT(@j, '$[1]', 'x') |
+------------------------------------+
| ["x","a", {"b": [1, 2]}, [3, 4]]  |
+------------------------------------+
在數組第100個值後面插入字符’x’ 元素,如果超長,則在最後一個值後面插入:
mysql> SELECT JSON_ARRAY_INSERT(@j, '$[100]', 'x');
+--------------------------------------+
| JSON_ARRAY_INSERT(@j, '$[100]', 'x') |
+--------------------------------------+
| ["a", {"b": [1, 2]}, [3, 4], "x"]    |
+--------------------------------------+
在數組第1個值中key爲’b’的value數組第1個值前面入字符元素’x’:
mysql> SELECT JSON_ARRAY_INSERT(@j, '$[1].b[0]', 'x');
+-----------------------------------------+
| JSON_ARRAY_INSERT(@j, '$[1].b[0]', 'x') |
+-----------------------------------------+
| ["a", {"b": ["x", 1, 2]}, [3, 4]]       |
+-----------------------------------------+
在數組第2個值後面數組第1個值後面入字符元素’y’:
mysql> SELECT JSON_ARRAY_INSERT(@j, '$[2][1]', 'y');
+---------------------------------------+
| JSON_ARRAY_INSERT(@j, '$[2][1]', 'y') |
+---------------------------------------+
| ["a", {"b": [1, 2]}, [3, "y", 4]]     |
+---------------------------------------+
數組元素的批量插入:
mysql> SELECT JSON_ARRAY_INSERT(@j, '$[0]', 'x', '$[2][1]', 'y');
+----------------------------------------------------+
| JSON_ARRAY_INSERT(@j, '$[0]', 'x', '$[2][1]', 'y') |
+----------------------------------------------------+
| ["x", "a", {"b": [1, 2]}, [3, 4]]                      |
+----------------------------------------------------+

3JSON_INSERT標準JSON值的插入

如果這個key已經存在,則不會去覆蓋舊值,不存在的插入:

mysql> SET @j = '{ "a": 1, "b": [2, 3]}';
mysql> SELECT JSON_INSERT(@j, '$.a', 10, '$.c', '[true, false]');
+----------------------------------------------------+
| JSON_INSERT(@j, '$.a', 10, '$.c', '[true, false]') |
+----------------------------------------------------+
| {"a": 1, "b": [2, 3], "c": "[true, false]"}          |
+----------------------------------------------------+

4JSON_MERGE合併操作

JSON_MERGE這個主要用於合併:如果兩個是數組的,則合併爲一個數組;如果兩個是JSON的,則合併爲一個JSON,如果兩個JSON有相同的key,則value合併爲數組;如果是兩個單值的,則合併爲一個數組;如果是一個數組,一個是JSON,則合併爲一個數組:

mysql> SELECT JSON_MERGE('[1, 2]', '[true, false]');
+---------------------------------------+
| JSON_MERGE('[1, 2]', '[true, false]') |
+---------------------------------------+
| [1, 2, true, false]                       |
+---------------------------------------+
mysql> SELECT JSON_MERGE('{"name": "x"}', '{"id": 47}');
+-------------------------------------------+
| JSON_MERGE('{"name": "x"}', '{"id": 47}') |
+-------------------------------------------+
| {"id": 47, "name": "x"}                       |
+-------------------------------------------+
mysql> SELECT JSON_MERGE('{"id": "x"}', '{"id": 47}');
+-------------------------------------------+
| JSON_MERGE('{"id": "x"}', '{"id": 47}') |
+-------------------------------------------+
| {"id": ["x",47]}                             |
+-------------------------------------------+
mysql> SELECT JSON_MERGE('1', 'true');
+-------------------------+
| JSON_MERGE('1', 'true') |
+-------------------------+
| [1, true]                  |
+-------------------------+
mysql> SELECT JSON_MERGE('[1, 2]', '{"id": 47}');
+------------------------------------+
| JSON_MERGE('[1, 2]', '{"id": 47}') |
+------------------------------------+
| [1, 2, {"id": 47}]                    |
+------------------------------------+

5JSON_REMOVE刪除操作

JSON_REMOVE刪除操作,這個比較簡單,刪除字段中某個值,數組的或是JSON格式的都可以:

mysql> SET @j = '["a", ["b", "c"], "d"]';
mysql> SELECT JSON_REMOVE(@j, '$[1]');
+-------------------------+
| JSON_REMOVE(@j, '$[1]') |
+-------------------------+
| ["a", "d"]                 |
+-------------------------+
mysql> SET @j = '{"name": "x","id": 47}';
mysql> SELECT JSON_REMOVE(@j, '$.id');
+-------------------------+
| JSON_REMOVE(@j, '$.id') |
+-------------------------+
| {"name": "x"}              |
+-------------------------+
mysql> SELECT JSON_REMOVE(@j, '$.id', '$.name');
+-----------------------------------+
| JSON_REMOVE(@j, '$.id', '$.name') |
+-----------------------------------+
| {}                                       |
+-----------------------------------+
mysql> SET @j = '{"name": "x","id": [47,48]}';
mysql> SELECT JSON_REMOVE(@j, '$.id[1]');
+-------------------------+
| JSON_REMOVE(@j, '$.id') |
+-------------------------+
| {"id": [47],"name": "x"} |
+-------------------------+

6JSON_REPLACE操作

JSON_REPLACE替換操作,有則替換,無則忽略。

mysql> SET @j = '{ "a": 1, "b": [2, 3]}';
mysql> SELECT JSON_REPLACE(@j, '$.a', 10, '$.c', '[true, false]');
+-----------------------------------------------------+
| JSON_REPLACE(@j, '$.a', 10, '$.c', '[true, false]') |
+-----------------------------------------------------+
| {"a": 10, "b": [2, 3]}                                    |
+-----------------------------------------------------+
mysql> SELECT JSON_REPLACE(@j, '$.a', 10, '$.b[1]',99);
+-----------------------------------------------------+
| JSON_REPLACE(@j, '$.a', 10, '$.b[1]',99') |
+-----------------------------------------------------+
| {"a": 10, "b": [2, 99]}                                    |
+-----------------------------------------------------+

7JSON_SET操作

JSON_SET操作爲更新和插入操作,已經存在的去更新;不存在則插入,以下是JSON_SET、JSON_INSERT、JSON_REPLACE執行結果的區別:

p_w_picpath_thumb26

四、JSON字段索引以及Generated字段

JSON字段類型在當前的版本中自身沒有索引,那麼在生產中是非常可怕的,JSON字段的增、刪、改、查效率可想而知,基本沒法用,也許是基於此,MySQL5.7中提供了Generated字段類型,網上有叫生成列或是計算列的。這裏先來了解一下什麼是Generated Column。

1Generated Column介紹

Generated Column是MySQL 5.7.6引入的新特性,所謂Cenerated Column,就是數據庫中這一列由其他列計算而得。引用官方參考手冊中的例子予以說明:

CREATE TABLE triangle (
  sidea DOUBLE,
  sideb DOUBLE,
  sidec DOUBLE AS (SQRT(sidea * sidea + sideb * sideb))
);
INSERT INTO triangle (sidea, sideb) VALUES(1,1),(3,4),(6,8);
mysql> SELECT * FROM triangle;
+-------+-------+--------------------+
| sidea | sideb | sidec              |
+-------+-------+--------------------+
|     1 |     1 | 1.4142135623730951 |
|     3 |     4 |                  5     |
|     6 |     8 |                 10     |
+-------+-------+--------------------+

Generated Column有兩種,即Virtual Generated Column和Stored Generated Column,前者只將Generated Column保存在數據字典中(表的元數據),並不會將這一列數據持久化到磁盤上;後者會將Generated Column持久化到磁盤上,而不是每次讀取的時候計算所得。很明顯,後者存放了可以通過已有數據計算而得到的數據,需要更多的磁盤空間,與Virtual Column相比並沒有優勢,因此,MySQL 5.7中,不指定Generated Column的類型,默認是Virtual Column。雖然一般情況下都應該使用Virtal Generated Column,但是,目前使用Virtual Generated Column還有很多限制:不能用作主鍵、不能作爲主鍵、不能創建全文索引和空間索引等,但是在後續的版本中可能支持,所以如果使用Generated Column字段做索引的話,還是使用Stored Generated Column吧,在使用Generated Column做索引上,JSON字段索引的解決方案,官方也是推薦使用Stored Generated Column。使用Stored Generated Column建表語句如下,只是在加了個單詞:

CREATE TABLE triangle (
  sidea DOUBLE,
  sideb DOUBLE,
  sidec DOUBLE AS (SQRT(sidea * sidea + sideb * sideb) STORED)
);

2Generated Column注意事項

Generated Column是不能進行寫操作的,它是自動生成的;在創建的時候要考慮這個列的計算公式是否合理,不合理的話創建的時候不會報錯,使用時插入值就會報錯;Generated Column依賴的列在刪除的時候會提示報錯,必須先刪除Generated Column才能再去刪除它依賴的列;Generated Column定義不合法,如我們將generated column定義爲 "x列+y列",很明顯,x列或y列都是數值型,如果我們將x列或y列定義(或修改)爲字符型,則預期會報錯,但實際上我們可以正常創建,但是在插入的時候是會報錯的。

p_w_picpath_thumb28

3、利用Generated ColumnJSON字段添加索引

正常情況下,JSON字段的相關查詢是掃描全表的,因爲JSON字段本身不能創建索引的,我們利用Generated Column特性,對JSON字段中相關key作爲Generated Column來做生成列,然後對Generated Column做索引:

ALTER TABLE json_test ADD COLUMN age INT AS (JSON_EXTRACT(user_info,'$.age')) STORED, ADD KEY idx_age (age);

前後對比圖如下:

p_w_picpath_thumb30

p_w_picpath_thumb32

可以很明顯的看出,使用Generated Column並添加索引後,查詢JSON字段中的值使用索引。

五、結束語

MySQL5.7中Generated Column和JSON Column的出現,使一些場景替代MongoDB等NoSQL提供了可能,雖然整體上還沒有MongoDB等做的那麼強大,但相信以後使用這兩種類型的場景會越來越多, 同時對DBA的挑戰也越來越大,希望密集使用JSON類型業務使用獨立的MySQL實例來運行,以免JSON成爲大字段(存儲在JSON文檔的大小JSON 列被限制爲值 max_allowed_packet的系統變量)時對其他業務帶來影響。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章