mysql虛擬列(Generated Columns)及JSON字段類型的使用

https://www.cnblogs.com/yjmyzz/p/json-type-and-virtual-column-usage-in-mysql-5-7.html

mysql 5.7中有很多新的特性,但平時可能很少用到,這里列舉2個實用的功能:虛擬列及json字段類型

一、先創(chuàng)建一個測試表:


`drop` `table`  `if exists t_people;`

`CREATE` `TABLE` `t_people(`

``id` ``INT``(11) ``NOT` `NULL` `AUTO_INCREMENT,`

````name``` ``varchar``(50) ``NOT` `NULL` `DEFAULT` `''``,`

``profile` json ``not` `null` `,`

``created_at` ``TIMESTAMP``(3) ``DEFAULT` `CURRENT_TIMESTAMP``(3) ``ON` `UPDATE` `CURRENT_TIMESTAMP``(3),`

``updated_at` ``TIMESTAMP``(3) ``DEFAULT` `CURRENT_TIMESTAMP``(3) ``ON` `UPDATE` `CURRENT_TIMESTAMP``(3),`

`PRIMARY` `KEY` `(id));`

注:這里profile是一個json類型的字段,另db編碼采用utf8mb4

二、生成測試數(shù)據(jù)

`delimiter //`

`-- 寫一段存儲過程,方便后面生成測試數(shù)據(jù)`

`create` `procedure` `batchInsert()`

`begin`

`declare` `i ``int``;`

`declare` `v_name ``varchar``(50);`

`declare` `v_profile ``varchar``(100);`

`set` `i=0;`

`while i<100000 do`

`set` `v_name = concat(``substring``(``'趙錢孫李周吳鄭王張楊'``,floor(1+(rand()*10)),1),``substring``(``'菩提樹下的楊過'``,floor(1+(rand()*7)),1),``substring``(``'我愛北京天安門'``,floor(1+(rand()*7)),1),i);`

`set` `v_profile  = concat(``"{\"phone\":\""``,concat(``'13'``,floor(1+(rand()*9)),floor(1+(rand()*9)),floor(1+(rand()*9)),floor(1+(rand()*9)),floor(1+(rand()*9)),floor(1+(rand()*9)),floor(1+(rand()*9)),floor(1+(rand()*9)),floor(1+(rand()*9))) , ``"\",\"age\":"``,i,``"}"``);`

`insert` `into` `t_people(```name```,profile) ``values``(v_name,v_profile);`

`set` `i=i+1;`

`end` `while;`

`end``; //`

注:這段存儲過程不是本文重點,看不懂的同學(xué)不用深研,大概意思就是name隨機生成,profile隨機生成一個類似{"phone":"13xxxxxx","age":x}的內(nèi)容。

調(diào)用一下這個存儲過程,生成100000條測試數(shù)據(jù),數(shù)據(jù)大致長下面這樣:

點擊查看原圖

需求來了,假如我們要查姓“張”的人有多少個?

點擊查看原圖

這顯然是一個全表掃描!

三、前綴索引

肯定有同學(xué)想到了,在name上建一個前綴索引,只對name的第1個字做索引

`alter` `table` `t_people ``add` `key` `ix_name(``name``(1));`

確實是個好辦法,效果也不錯

點擊查看原圖

但是需求總是變化的,如果想查第2個字是“楊”的人有多少?

點擊查看原圖

依然會全表掃描。

四、虛擬列

`alter` `table` `t_people ``add` `second_name ``varchar``(3) generated always ``as``(``substring``(``name``,2,1)) stored;`

創(chuàng)建了一個虛擬列second_name,其值是substring(name,2,1),即name中的第2個字,最后的stored表示,數(shù)據(jù)寫入時這個列的值就會計算(詳情可參考最后的參考鏈接)

注:虛擬列并不是真正的列,insert時也無法指定字段值。

然后在這個列上創(chuàng)建索引:

`alter` `table` `t_people ``add` `index` `ix_second_name(`second_name`);`

再來看下執(zhí)行計劃,索引生效了,掃描行數(shù)也明顯下降。

點擊查看原圖

當(dāng)然,sql語句也可以改成:

`explain ``select` `count``(0) ``from` `t_people ``where` `second_name=``'楊'``;`

這樣看上去更直觀,效果不變。

五、json檢索

又來新需求了:要查profile中手機號為13589135467,并且姓“吳”的人

點擊查看原圖

注意:profile->"$.phone"=xxx 就是json字段的檢索語法

點擊查看原圖

分析執(zhí)行計劃,可以看到前綴索引“ix_name”生效了,但還有優(yōu)化空間,仍然可以借助虛擬列,創(chuàng)建2個虛擬列phone、first_name,并創(chuàng)建聯(lián)合索引。

`alter` `table` `t_people ``add` `first_name ``varchar``(3) generated always ``as``(``substring``(``name``,1,1)) stored;`

`alter` `table` `t_people ``add` `phone ``varchar``(20) generated always ``as``(profile->``"$.phone"``) stored;`

`alter` `table` `t_people ``add` `index` `ix_phone_firstname(phone,first_name);`

加了這2個虛擬列后,數(shù)據(jù)長這樣:

點擊查看原圖

注:phone列提取出來后,前后會帶上引號。

剛才的需求,可以改寫sql:

`select` `* ``from` `t_people ``where` `phone=``'\"13589135467\"'` `and` `name` `like` `'吳%'``;`

最后看下執(zhí)行計劃:

點擊查看原圖

掃描行數(shù)下降到個位數(shù),效果十分明顯。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容