1. 程式人生 > 實用技巧 >mysql虛擬列(Generated Columns)及JSON欄位型別的使用

mysql虛擬列(Generated Columns)及JSON欄位型別的使用

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

一、先建立一個測試表:

1

2

3

4

5

6

7

8

9

droptableif exists t_people;

CREATETABLEt_people(

`id`INT(11)NOTNULLAUTO_INCREMENT,

`name`varchar(50)NOTNULLDEFAULT'',

`profile` jsonnotnull,

`created_at`TIMESTAMP(3)DEFAULTCURRENT_TIMESTAMP(3)ONUPDATECURRENT_TIMESTAMP

(3),

`updated_at`TIMESTAMP(3)DEFAULTCURRENT_TIMESTAMP(3)ONUPDATECURRENT_TIMESTAMP(3),

PRIMARYKEY(id));

注:這裡profile是一個json型別的欄位,另db編碼採用utf8mb4 

二、生成測試資料

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

delimiter //

-- 寫一段儲存過程,方便後面生成測試資料

createprocedurebatchInsert()

begin

declareiint;

declarev_namevarchar(50);

declarev_profilevarchar(100);

seti=0;

while i<100000 do

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

setv_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,"}");

insertintot_people(`name`,profile)values(v_name,v_profile);

seti=i+1;

endwhile;

end; //

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

呼叫一下這個儲存過程,生成100000條測試資料,資料大致長下面這樣:

點選檢視原圖

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

點選檢視原圖

這顯然是一個全表掃描!

三、字首索引

肯定有同學想到了,在name上建一個字首索引,只對name的第1個字做索引

1

altertablet_peopleaddkeyix_name(name(1));

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

點選檢視原圖

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

點選檢視原圖

依然會全表掃描。

四、虛擬列

1

altertablet_peopleaddsecond_namevarchar(3) generated alwaysas(substring(name,2,1)) stored;

建立了一個虛擬列second_name,其值是substring(name,2,1),即name中的第2個字,最後的stored表示,資料寫入時這個列的值就會計算(詳情可參考最後的參考連結)

注:虛擬列並不是真正的列,insert時也無法指定欄位值。

然後在這個列上建立索引:

1

altertablet_peopleaddindexix_second_name(`second_name`);

再來看下執行計劃,索引生效了,掃描行數也明顯下降。

點選檢視原圖

當然,sql語句也可以改成:

1

explainselectcount(0)fromt_peoplewheresecond_name='楊';

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

五、json檢索

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

點選檢視原圖

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

點選檢視原圖

分析執行計劃,可以看到字首索引“ix_name”生效了,但還有優化空間,仍然可以藉助虛擬列,建立2個虛擬列phone、first_name,並建立聯合索引。

1

2

3

altertablet_peopleaddfirst_namevarchar(3) generated alwaysas(substring(name,1,1)) stored;

altertablet_peopleaddphonevarchar(20) generated alwaysas(profile->"$.phone") stored;

altertablet_peopleaddindexix_phone_firstname(phone,first_name);

加了這2個虛擬列後,資料長這樣: 

點選檢視原圖

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

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

1

select*fromt_peoplewherephone='\"13589135467\"'andnamelike'吳%';

最後看下執行計劃:  

點選檢視原圖

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

參考文章:

http://mysqlserverteam.com/generated-columns-in-mysql-5-7-5/

https://dev.mysql.com/doc/refman/5.7/en/json.html