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
替换为与该表达式匹配的生成列的名称。这在 EXPLAIN
显示的扩展 SHOW WARNINGS
信息中的重写查询中也很明显
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()
,则优化器只会为第一个比较匹配索引。如果优化器选择了错误的索引,则可以使用索引提示来禁用它,并强制优化器做出不同的选择。