MySQL 支援產生式資料行的索引。例如
CREATE TABLE t1 (f1 INT, gc INT AS (f1 + 1) STORED, INDEX (gc));
產生式資料行 gc
定義為運算式 f1 + 1
。該資料行也已建立索引,而優化器可以在執行計畫建構期間考慮該索引。在下列查詢中,WHERE
子句會參考 gc
,而優化器會考量該資料行上的索引是否產生更有效率的計畫
SELECT * FROM t1 WHERE gc > 9;
即使查詢中沒有直接依名稱參考產生式資料行,優化器仍然可以使用這些資料行的索引來產生執行計畫。如果 WHERE
、ORDER BY
或 GROUP BY
子句參考與某些已編索引的產生式資料行定義相符的運算式,就會發生這種情況。下列查詢不會直接參考 gc
,但會使用與 gc
定義相符的運算式
SELECT * FROM t1 WHERE f1 + 1 > 9;
優化器會識別出運算式 f1 + 1
與 gc
的定義相符,且 gc
已編索引,因此它會在執行計畫建構期間考慮該索引。您可以使用 EXPLAIN
來查看此情況
mysql> EXPLAIN SELECT * FROM t1 WHERE f1 + 1 > 9\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
partitions: NULL
type: range
possible_keys: gc
key: gc
key_len: 5
ref: NULL
rows: 1
filtered: 100.00
Extra: Using index condition
實際上,優化器已將運算式 f1 + 1
取代為與該運算式相符的產生式資料行名稱。這在 SHOW WARNINGS
顯示的擴充 EXPLAIN
資訊中提供的重寫查詢中也很明顯
mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select `test`.`t1`.`f1` AS `f1`,`test`.`t1`.`gc`
AS `gc` from `test`.`t1` where (`test`.`t1`.`gc` > 9)
以下限制和條件適用於優化器使用產生式資料行索引
為了讓查詢運算式與產生式資料行定義相符,運算式必須完全相同,且必須具有相同的結果類型。例如,如果產生式資料行運算式為
f1 + 1
,則如果查詢使用1 + f1
,或如果將f1 + 1
(整數運算式) 與字串進行比較,優化器將無法識別為相符。最佳化適用於以下運算子:
=
、<
、<=
、>
、>=
、BETWEEN
和IN()
。對於
BETWEEN
和IN()
以外的運算子,任一運算元都可以替換為相符的產生欄位。對於BETWEEN
和IN()
,只有第一個引數可以替換為相符的產生欄位,而其他引數必須具有相同的結果類型。BETWEEN
和IN()
尚不支援涉及 JSON 值的比較。產生欄位必須定義為包含至少一個函式呼叫或前述項目中提及的其中一個運算子的運算式。運算式不能僅包含對另一個欄位的簡單參照。例如,
gc INT AS (f1) STORED
僅包含欄位參照,因此不考慮gc
上的索引。對於將字串與從傳回帶引號字串的 JSON 函式計算值的索引產生欄位進行比較的情況,欄位定義中需要
JSON_UNQUOTE()
以移除函式值中多餘的引號。(若直接將字串與函式結果進行比較,JSON 比較器會處理引號移除,但索引查詢不會發生此情況。)例如,不要寫成這樣的欄位定義doc_name TEXT AS (JSON_EXTRACT(jdoc, '$.name')) STORED
應寫成這樣
doc_name TEXT AS (JSON_UNQUOTE(JSON_EXTRACT(jdoc, '$.name'))) STORED
使用後者的定義,最佳化工具可以偵測到這兩個比較都符合
... WHERE JSON_EXTRACT(jdoc, '$.name') = 'some_string' ... ... WHERE JSON_UNQUOTE(JSON_EXTRACT(jdoc, '$.name')) = 'some_string' ...
若欄位定義中沒有
JSON_UNQUOTE()
,最佳化工具只會偵測到第一個比較符合。如果最佳化工具選擇了錯誤的索引,可以使用索引提示來停用它並強制最佳化工具做出不同的選擇。