MySQL中支持生成列,生成列的值是根據列定義中包含的表達式計算的。
一個簡單的例子來認識生成列!
CREATE TABLE triangle( sidea DOUBLE, sideb DOUBLE, sidec DOUBLE AS (SQRT(sidea * sidea + sideb * sideb)) ); INSERT INTO triangle(sidea, sideb) VALUES(3,4),(6,8),(5,12); mysql> select * from triangle; #插入數值的時候並沒有插入c的值,但是查詢的時候,還是有了c值 +-------+-------+-------+ | sidea | sideb | sidec | +-------+-------+-------+ | 3 | 4 | 5 | | 6 | 8 | 10 | | 5 | 12 | 13 | +-------+-------+-------+ 3 rows in set (0.00 sec) mysql>
生成列中的值,是根據生成列的定義計算出來的!
生成列的定義如下:
col_name data_type [GENERATED ALWAYS] AS (expression) [VIRTUAL | STORED] [NOT NULL | NULL] [UNIQUE [KEY]] [[PRIMARY] KEY] [COMMENT 'string']
#AS表達式表示生成列並用於計算生成列的表達式。As之前的GENERATED ALWAYS可以讓生成列的性質更明確(應該沒什么作用,反正是可以省略的)!
#VIRTUAL | STORED 關鍵字表示列值的存儲方式
-
VIRTUAL: 不存儲列值,但在讀取值之前立即計算此列的數值,虛擬列不占存儲空間,innodb支持虛擬列上的二級索引。為默認的存儲方式。
- STORED:插入或更新時,將計算存儲列的值,並且占用存儲空間。
生成列的表達式必須遵循以下的規則,否則會報錯:
- 允許使用字符串,運算符和確定性內置函數。對於給定的相同的數據庫,不同連接的用戶調用會產生相同的結果,則這個函數時確定的。now()函數就不是確定性函數。
- 不允許使用子查詢,參數,變量,存儲函數(stored functions)和用戶自定義的函數。
- 生成的列定義可以引用其他生成的列,但只能引用表定義中較早出現的列。生成的列定義可以引用表中的任何基本(非生成)列,無論其定義是早期還是稍后發生。
- auto_increment屬性不能再生成列的定義中使用。
- 一個
AUTO_INCREMENT
列不能用作在生成的列定義的基柱(AnAUTO_INCREMENT
column cannot be used as a base column in a generated column definition.) - 從MySQL 5.7.10開始,如果表達式求值導致截斷或向函數提供不正確的輸入,則
CREATE TABLE
語句將以錯誤終止並拒絕DDL操作
生成列的作用(可能不太准確):
- 虛擬生成的列可用作簡化和統一查詢的方法。可以將復雜條件定義為生成列,並從表上的多個查詢引用,以確保他們全部使用完全相同的條件。
- 存儲生成的列可以用作物化緩存,用於復雜的條件,這些條件在運行中計算成本很高。
- 生成列可以模擬功能索引,利用生成列定義功能表達式對其進行索引。例如對JSON數據類型。對於存儲的生成列,這種方法缺點是存儲兩次,一次是生成列的值,另一次是索引。
- 如果生成的列已編制索引,則優化程序將識別與列定義匹配的查詢表達式,並在查詢執行期間根據需要使用列中的索引,即使查詢未按名稱直接引用該列也是如此。
如下一個含有json類型字段的表:
CREATE TABLE json_test ( id INT auto_increment PRIMARY KEY, userinfo json ); #插入數據 INSERT INTO json_test(userinfo) VALUES('{"name":"libai","address":"china","email":"libai@163.com"}'); INSERT INTO json_test(userinfo) VALUES('{"name":"obama","address":"miguo","email":"libai@gmail.com"}'); INSERT INTO json_test(userinfo) VALUES('{"name":"putin","address":"russia","email":"putin@gmail.com"}'); #
給表中添加生成列
alter table json_test add column user_name varchar(40) generated always as (userinfo->"$.name") virtual; #查看表中的數據 mysql> select * from json_test; +----+--------------------------------------------------------------------+-----------+ | id | userinfo | user_name | +----+--------------------------------------------------------------------+-----------+ | 1 | {"name": "libai", "email": "libai@163.com", "address": "china"} | "libai" | | 2 | {"name": "obama", "email": "libai@gmail.com", "address": "miguo"} | "obama" | | 3 | {"name": "putin", "email": "putin@gmail.com", "address": "russia"} | "putin" | +----+--------------------------------------------------------------------+-----------+ 3 rows in set (0.00 sec) #在表中插入數據user_name列的數值會自動計算,這時候,我們就可以對user_name列添加索引!