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ù),效果十分明顯。