优化器对生成的列索引的使用
MySQL支持在生成的列上建立索引。例如:
CREATE TABLE t1 (f1 INT, gc INTAS (f1 + 1)STORED ,INDEX (gc));
生成的列gc
定义为表达式f1 + 1
。该列也被索引,优化器可以在执行计划构建期间考虑该索引。在以下查询中,该WHERE
子句引用gc
并且优化器考虑该列上的索引是否产生更有效的计划:
SELECT *FROM t1WHERE gc > 9;
即使在按名称查询这些列时没有直接引用的情况下,优化器也可以使用所生成列的索引来生成执行计划。会发生此如果WHERE
,ORDER BY
或GROUP BY
条款是指一些索引生成列的定义相匹配的表达式。以下查询未直接引用,gc
但使用与以下定义匹配的表达式gc
:
SELECT *FROM t1WHERE f1 + 1 > 9;
优化器认识到表达式f1 + 1
与的定义匹配gc
并且gc
被索引,因此它在执行计划构建期间会考虑该索引。您可以使用EXPLAIN
以下命令参见:
mysql>EXPLAIN SELECT *FROM t1WHERE 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 Extr : 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
不考虑索引on 。 为了将字符串与索引生成的列进行比较,索引生成的列从返回带引号的字符串的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()
列定义中,则优化器仅针对这些比较中的第一个比较检测到匹配项。- 如果优化器选择了错误的索引,则可以使用索引提示将其禁用,并强制优化器做出其他选择。