mysql 之 json 資料型別的使用及高效檢索(配合虛擬列 virtual generated column)
mysql 5.7+ 版本開始支援 json 資料型別,可以方便的儲存JSON格式的資料,同時配合虛擬列 (virtual generated column),可以方便的為 json 列資料的某屬性對映虛擬列,建立索引,高效檢索。
構造json資料
方法:json_array() / json_object()
json_array / json_object 用於組裝 json 資料,json 說的簡單些json就是由標量(int, float, string) + 陣列 + 物件組合而成的,這兩個函式可以方便的用於構造陣列和物件的json格式串
json_array(item1, item2, item3, ...) => [item1, item2, item3] json_object(key1, val1[, [key2, val2]...]) => {"key1": "val1", "key2": "val2",...}
使用場景例如:
select json_object( "username", "big_cat", "favorites", json_array( json_object("article_id", 1, "favorited_at", "2019-01-18"), json_object("article_id", 2, "favorited_at", "2019-01-18"), json_object("article_id", 3, "favorited_at", "2019-01-18"), json_object("article_id", 4, "favorited_at", "2019-01-18") ) ); // result { "username": "big_cat", "favorites": [ {"article_id": 1, "favorited_at": "2019-01-18"}, {"article_id": 2, "favorited_at": "2019-01-18"}, {"article_id": 3, "favorited_at": "2019-01-18"}, {"article_id": 4, "favorited_at": "2019-01-18"} ] }
讀取json資料
方法:json_extract() /col
->"$.{property_name}"
json_extract 用於讀取 json 列的某欄位,或者也可以使用col
->"$.{property_name}" 的方式訪問
json_extract(`col`, '$.{property_name}') / `col`->'$.{property_name}'
create table `users` ( `id` int unsigned not null auto_increment primary key, `doc` json ); insert into `users`(`doc`) values (json_object("name", "big_cat", "age", 28)), ('{"name": "james", "age": 29}'); select json_extract(`doc`, "$.name") as `name`, json_extract(`doc`, "$.age") as `age` from `users`; select `doc`->"$.name" as `name`, `doc`->"$.age" as `age` from `users`;
高效檢索json資料
mysql 提供的一些函式是可以方便我們條件檢索json資料的,但無法使用索引,資料量大的時候難免低效。
select id, doc->"$.age" from users where json_extract(doc, "$.name") = "big_cat"; select id, doc->"$.age" from users where doc->"$.name" = "big_cat";
這時我們可以利用同 json 一同新增的特性:虛擬列(virtual generated column)。
將需要參與檢索的 json 屬性對映為 虛擬列,在虛擬列上建立索引,便可參與高效檢索。
另外補充一下,在mysql 5.7+中,支援兩種Generated Column,即Virtual Generated Column和Stored Generated Column。前者不儲存元資料,後者會將 expression 的計算結果實際的儲存下來。其實二者效能差距並不大,若對二者建立索引進行檢索操作,前者效能可能會略低於後者,因為前者要對結果集即時的進行 expression 的演算,但後者需要消耗額外的儲存空間。
需要注意的有:不儲存資料的特性也導致只能在虛擬列上建立二級索引,插入資料時不可以向虛擬列插入數值(mysql自行負責演算)。
#虛擬列建立 ALTER TABLE `table_name` ADD COLUMN `col_name` <type> [ GENERATED ALWAYS ] AS ( <expression> ) [ VIRTUAL|STORED ] [ UNIQUE [KEY] ] [ [PRIMARY] KEY ] [ NOT NULL ] [ COMMENT <text> ]
# 為 user 表的 json 欄位的 name 建立虛擬列 alter table `users` add column `user_name` varchar(10) generated always as (`doc`->"$.name"); # 為虛擬列新增索引 alter table `users` add index `index_u_n`(`user_name`); # 檢索時可以使用索引 explain select `id`, `user_name`, `doc`->"$.age" as `age` from `users` where `user_name` = "big_cat" \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: users partitions: NULL type: ref possible_keys: index_u_n key: index_u_n key_len: 43 ref: const rows: 1 filtered: 100.00 Extra: NULL 1 row in set, 1 warning (0.00 sec)
下面直接對 json 解析檢索的方式是無法用到索引的
explain select id from users where doc->"$.user_name" = "big_cat" \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: users partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 2 filtered: 100.00 Extra: Using where 1 row in set, 1 warning (0.00 sec) explain select id from users where json_extract(`doc`, '$.username') = "big_cat" \G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: users partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 2 filtered: 100.00 Extra: Using where 1 row in set, 1 warning (0.00 sec)
還有其他 json 的使用這裡就不說明了,大家可以參考一下文章:
mysql json 使用 型別 查詢 函式:ofollow,noindex" target="_blank">https://www.cnblogs.com/ooo0/...
MySQL 5.7 虛擬列 (virtual columns):https://www.cnblogs.com/raich...
MySQL 5.7原生JSON格式支援:https://www.cnblogs.com/zouca...