概叙

MySQL 8.0.13引入了函数索引,在MySQL之前版本中,未支持函数索引,被人诟病,虽然可以通过generated column实现类似功能,但并不完美和方便。

MySQL 8.0.13引入函数索引主要解决某些场景下的查询优化问题,通常来说索引使用的是列值或者列值的前缀部分。后面我们也可以看看subtr(col1,1,10)的函数索引和前缀索引INDEX (col1(10))。

MySQL :: WL#1075: Add support for functional indexes

MySQL :: MySQL 8.0 Reference Manual :: 15.1.15 CREATE INDEX Statement

MySQL函数索引

MySQL中的函数索引是指对数据库表中的列使用函数时,可以创建函数索引以优化查询性能。函数索引通常用于 TEXT 或 BLOB 数据类型,因为这些类型的数据可能需要进行全文搜索。

创建索引语法参考官方文档:MySQL :: MySQL 8.0 Reference Manual :: 15.1.15 CREATE INDEX Statement

CREATE [UNIQUE | FULLTEXT | SPATIAL] INDEX index_name
    [index_type]
    ON tbl_name (key_part,...)
    [index_option]
    [algorithm_option | lock_option] ...

key_part: {col_name [(length)] | (expr)} [ASC | DESC]

index_option: {
    KEY_BLOCK_SIZE [=] value
  | index_type
  | WITH PARSER parser_name
  | COMMENT 'string'
  | {VISIBLE | INVISIBLE}
  | ENGINE_ATTRIBUTE [=] 'string'
  | SECONDARY_ENGINE_ATTRIBUTE [=] 'string'
}

index_type:
    USING {BTREE | HASH}

algorithm_option:
    ALGORITHM [=] {DEFAULT | INPLACE | COPY}

lock_option:
    LOCK [=] {DEFAULT | NONE | SHARED | EXCLUSIVE}

创建函数索引的语法示例如下:

CREATE INDEX index_name ON table_name (FUNCTION(column_name));

这里的 FUNCTION 可以是 MD5SHA1SHA2CRC32ELT, 等MySQL支持的函数。

例如,如果你有一个 users 表,其中包含一个 bio 列,你想要对 bio 列的每个单词的首字母创建一个索引,你可以使用如下语句:

CREATE INDEX idx_first_letter ON users (ELT(1, SUBSTRING(bio, 1, LOCATE(' ', bio, 1)- 1)));

这个例子创建了一个函数索引,它使用 ELT 和 SUBSTRING 函数来提取 bio 列中每个单词的首字母,并用于快速查询以找到具有特定首字母的用户。

请注意,函数索引在某些情况下可能导致查询性能下降,因为它们不总是能够被优化器有效地使用。此外,不是所有的函数都可以被用于创建函数索引,这取决于具体的数据库系统版本和存储引擎。

函数索引实现原理

在MySQL中,函数索引是一个虚拟的索引,它不是物理存在的,而是通过函数表达式来创建的。函数索引的实现通常涉及到函数索引的定义、创建和使用。

函数索引通常用于在存储的表达式上建立索引,这样的索引可以在查询中使用该表达式时提高查询的效率。

以下是创建函数索引的基本步骤:

  1. 创建表时,使用表达式来定义列。

  2. 创建一个包含这个表达式的函数索引。

  3. 在查询中使用这个表达式,并且让MySQL优化器利用这个函数索引来加快查询速度。

例如,假设我们有一个表orders,它有一个日期列order_date和一个计算列year(order_date),我们可以在这个计算列上创建一个函数索引:

CREATE TABLE orders (
    order_date DATE,
    order_year INT GENERATED ALWAYS AS (YEAR(order_date)) STORED
);
 
CREATE INDEX idx_order_year ON orders (order_year);

在这个例子中,order_year是一个计算列,它是order_date列的年份部分。我们在这个计算列上创建了一个索引idx_order_year

在查询时,如果查询中包含对order_year的使用,比如SELECT * FROM orders WHERE order_year = 2021,MySQL优化器可能会利用这个索引来提高查询的效率。

请注意,函数索引的实际效果可能会受到多种因素的影响,包括数据分布、查询模式、索引维护成本等。因此,在实际应用中,可能需要进行详细的性能测试来确定是否使用函数索引能达到预期的效果。

该特性的实现思路是针对索引上被括号包围的表达式建立隐藏的虚拟列(virtual generated column),并在虚拟列上创建索引,这些功能早已经存在了,因此这个worklog主要做了几件事情:

语法支持

扩展新的语法,允许在创建索引时使用表达式
索引内的表达式被翻译成创建列的操作(Create_field), 索引上每个表达式各对应一个虚拟列

自动创建虚拟列

这个功能的核心就是讲索引创建引用的表达式转换成虚拟列并隐藏处理,因此在创建索引之前要进行预处理,入口函数

mysql_prepare_create_table
    -->add_functional_index_to_create_list

在获得表达式后,需要根据表达式来推导列类型,由于代码中已经有为create table as select推导列类型, 所以这里复用了其中的代码,单独抽出来函数create_table_from_items中的代码refactor到Create_field *generate_create_field

虚拟列的命名为计算 md5(index name + key part number), 参考函数: make_functional_index_column_name
如上例:

root@test 12:10:02>SET SESSION debug="+d,show_hidden_columns";
Query OK, 0 rows affected (0.00 sec)

root@test 12:12:54>show create table t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  `c` int(11) DEFAULT NULL,
  `3bb8c14d415110ac3b3c55ce9108ae2d` bigint(12) GENERATED ALWAYS AS ((`a` + `b`)) VIRTUAL,
  `0d1cbc68e8957783288d2b71268047c7` bigint(12) GENERATED ALWAYS AS ((`a` + `b`)) VIRTUAL,
  `0d8d996e0f781cf4e749dfa71efc17ba` bigint(12) GENERATED ALWAYS AS ((`a` - `b`)) VIRTUAL,
  `e0a812eddbaed00becd72bf920eccab8` bigint(12) GENERATED ALWAYS AS ((`a` + `b`)) VIRTUAL,
  KEY `functional_index` (((`a` + `b`))),
  KEY `functional_index_2` (((`a` + `b`)),((`a` - `b`))),
  KEY `functional_index_3` (((`a` + `b`)),`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

隐藏虚拟列

dd::Column::enum_hidden_type m_hidden: 增加该属性,用于区分是用户定义的generated column还是自动生成的。据此判断该列是否能够对用户可见或者是否可以被删除等等

  enum class enum_hidden_type {
    /// The column is visible (a normal column)
    HT_VISIBLE = 1,
    /// The column is completely invisible to the server
    HT_HIDDEN_SE = 2,
    /// The column is visible to the server, but hidden from the user.
    /// This is used for i.e. implementing functional indexes.
    HT_HIDDEN_SQL = 3
  };

增加两个接口函数来判断是否是隐藏列:

  1. is_field_for_functional_index(), 用于:
"ALTER TABLE tbl DROP COLUMN;" 当列是隐藏列时,会抛出错误, ref: is_field_used_by_functional_index
Item_field::print() : 打印列的表达式而非列名, ref: get_field_name_or_expression
  1. is_hidden_from_user()
"INSERT INTO tbl;" without a column list, ref: insert_fields, Sql_cmd_insert_base::prepare_inner()
"SELECT * FROM tbl;"
"SHOW CREATE TABLE tbl;" and "SHOW FIELDS FROM tbl;",  ref : store_create_info

DDL:

prepare_create_field()
-当尝试加一个和隐藏列相同名字的列时,抛出错误

创建索引名:
当用户未指定列名时,server会自动创建列名,对于functional index和普通索引不太一样:因为列名是索引名和在索引上的key number产生的hash值,因此必须在生成虚拟列之前产生索引名.

mysql_alter_table:

  • 当删除索引时,相应的隐藏虚拟列也必须删除, ref: handle_drop_functional_index
  • 当rename索引名时,隐藏列名也必须重新计算并重命名, ref: handle_rename_functional_index

报错

Functional_index_error_handler: 隐藏虚拟列上的错误或warning信息, 转换成索引错误信息

函数索引的限制

  • 主键上无法建functional index
  • 可以混合普通key和functional key
  • 表达式需要定义在括号()内,但类似这样INDEX ((col1), (col2))定义是不允许的
  • functional key不允许选做外键
  • Spatial/Fulltext index不允许functional key
  • 如果列被某个functional index引用,需要先删除索引,才能删列
  • 不允许直接引用列前缀,但可以通过函数substring/cast来workaround
  • 对于一个unique的functional index,不能隐式转换为表的主键

详细不同,可参考官网:MySQL :: MySQL 8.0 Reference Manual :: 15.1.15 CREATE INDEX Statement

blob和text这些大字段类型上创建函数索引失败。 

root@test 10:27:48>CREATE TABLE tb (col longtext, key(col));
ERROR 1170 (42000): BLOB/TEXT column 'col' used in key specification without a key length
root@test 10:28:15>CREATE TABLE tb (col longtext, key((substring(col, 1, 10)));
Query OK, 0 rows affected (0.12 sec)

MySQL 8.0.13 以及更高版本支持函数索引(functional key parts),也就是将表达式的值作为索引的内容,而不是列值或列值前缀。将函数作为索引键可以用于索引那些没有在表中直接存储的内容。例如:

CREATE TABLE t1 (col1 INT, col2 INT, INDEX func_index ((ABS(col1))));
CREATE INDEX idx1 ON t1 ((col1 + col2));
CREATE INDEX idx2 ON t1 ((col1 + col2), (col1 - col2), col1);
ALTER TABLE t1 ADD INDEX ((col1 * 40) DESC);

多列索引可以同时包含非函数列和函数列。

函数索引支持ASC和DESC选项。

函数索引必须遵循以下规则。如果索引键中包含了不允许的内容,创建索引时将会产生错误。

在索引定义中,需要将表达式放入括号之中,以便与列值索引或者前缀索引进行区分。例如,以下索引表达式使用了括号:

INDEX ((col1 + col2), (col3 – col4))

下面是一个错误的写法,表达式没有位于括号之中:

INDEX (col1 + col2, col3 – col4)

函数索引不能只包含一个单独的列名。例如,以下写法是错误的:

INDEX ((col1), (col2))

但是,可以使用非函数索引的方式进行定义:

INDEX (col1, col2)

函数索引中的表达式不能使用列的前缀。可以使用 SUBSTRING() 和 CAST() 函数作为一个替代方案,参考后文。

使用限制:

外键不支持函数索引。

对于CREATE TABLE … LIKE语句,新建的表中将会保留源表中的函数索引。

函数索引实际上是使用隐藏的虚拟计算列来实现,存在以下限制:

每个函数索引都会算作一个列数,参与计算表的总列数限制;

函数索引同样遵循计算列的所有限制。

例如:

只有那些能够用于计算列的函数才能够用于创建函数索引。

函数索引中不允许使用子查询、参数、变量、存储函数以及自定义函数。

函数索引支持UNIQUE选项。

但是,主键不能包含函数列。

主键只能使用存储的计算列,但是函数索引使用虚拟计算列实现,而不是存储计算列。

空间索引和全文索引不支持函数索引。

如果某个表中没有主键,InnoDB 存储引擎自动将第一个 UNIQUE NOT NULL 索引提升为主键。但是对于包含函数列的 UNIQUE NOT NULL 索引不会进行提升。

对于非函数索引,如果创建重复的索引,系统会提示一个警告。创建重复的函数索引不会提示任何信息。

如果要删除一个在函数索引中使用的字段,必须先删除该索引;否则将会产生错误。

虽然非函数索引支持前缀索引,但是函数索引不支持使用字段的前缀。替代的方法就是使用 SUBSTRING() 函数(或者后文中的 CAST() 函数)。如果使用 SUBSTRING() 函数定义索引列,要想在查询中使用该索引,必须在WHERE 子句中使用同样的 SUBSTRING() 函数。

以下示例中,只有第二个SELECT能够使用索引,因为它的查询中使用了和索引定义相同的 SUBSTRING() 函数和参数:

CREATE TABLE tbl (
  col1 LONGTEXT,
  INDEX idx1 ((SUBSTRING(col1, 1, 10)))
);
 
SELECT * FROM tbl WHERE SUBSTRING(col1, 1, 9) = '123456789';
SELECT * FROM tbl WHERE SUBSTRING(col1, 1, 10) = '1234567890';

函数索引能够支持其他方式无法使用的数据类型,例如 JSON 数据。不过,使用时需要特别小心。例如,以下创建索引的语法不会生效:


CREATE TABLE employees (
  data JSON,
  INDEX ((data->>'$.name'))
);
 
ERROR 3757 (HY000): Cannot create a functional index on an expression that returns a BLOB or TEXT. Please consider using CAST.

该语法的问题在于:

运算符 ->> 等价于 JSON_UNQUOTE(JSON_EXTRACT(…))。

JSON_UNQUOTE() 函数返回 LONGTEXT 类型的数据,因此相应的隐藏计算列也具有这种数据类型。

MySQL 不支持非前缀的 LONGTEXT 列索引,而函数索引又不支持前缀索引。两者互相矛盾。

如果需要为 JSON 列创建索引,可以尝试使用 CAST() 函数:

CREATE TABLE employees (
  data JSON,
  INDEX ((CAST(data->>'$.name' AS CHAR(30))))
);

相应的隐藏计算列被转换为 VARCHAR(30) 类型,这种数据类型可以进行索引。但是这种方法带来了一个新的使用上的问题:

CAST() 函数返回的字符串使用 utf8mb4_0900_ai_ci 排序规则(服务器默认设置)。

JSON_UNQUOTE() 函数返回的字符串使用 utf8mb4_bin 排序规则(硬编码,不能修改)。

结果就是,索引定义中的字符排序与以下查询中的 WHERE 子句中的字符排序不一致:

SELECT * FROM employees WHERE data->>'$.name' = 'James';

以上查询不会使用索引。为了支持这种情况下能够使用函数索引,优化器查找索引时自动排除索引中的 CAST() 函数的影响,但是只有当索引表达式的排序规则能够匹配查询表达式的排序规则时才会这样处理。为了能够使用这种函数索引,可以采用以下两种解决方案之一(它们之间存在一些差异):

解决方案 1:为索引表达式指定一个与 JSON_UNQUOTE() 相同的字符排序规则:

CREATE TABLE employees (
  data JSON,
  INDEX idx ((CAST(data->>"$.name" AS CHAR(30)) COLLATE utf8mb4_bin))
);
 
INSERT INTO employees VALUES
  ('{ "name": "james", "salary": 9000 }'),
  ('{ "name": "James", "salary": 10000 }'),
  ('{ "name": "Mary", "salary": 12000 }'),
  ('{ "name": "Peter", "salary": 8000 }');
 
SELECT * FROM employees WHERE data->>'$.name' = 'James';

运算符 ->> 等价于 JSON_UNQUOTE(JSON_EXTRACT(…)) ,而 JSON_UNQUOTE() 返回的字符串使用 utf8mb4_bin 排序规则。因此,查询条件区分大小写,只返回一条记录:

+------------------------------------+
| data                               |
+------------------------------------+
| {"name": "James", "salary": 10000} |
+------------------------------------+

解决方案 2:在查询条件中指定完整的表达式:

CREATE TABLE employees (
  data JSON,
  INDEX idx ((CAST(data->>"$.name" AS CHAR(30))))
);
 
INSERT INTO employees VALUES
  ('{ "name": "james", "salary": 9000 }'),
  ('{ "name": "James", "salary": 10000 }'),
  ('{ "name": "Mary", "salary": 12000 }'),
  ('{ "name": "Peter", "salary": 8000 }');
 
SELECT * FROM employees WHERE CAST(data->>'$.name' AS CHAR(30)) = 'James';

CAST() 函数返回的字符串使用的是 utf8mb4_0900_ai_ci 排序规则,因此查询条件不区分大小写,返回两条记录:

+------------------------------------+
| data                               |
+------------------------------------+
| {"name": "james", "salary": 9000}  |
| {"name": "James", "salary": 10000} |
+------------------------------------+

需要注意的是,虽然优化器支持计算列索引中的自动 CAST() 去除处理, 不能使用以下方法实现 JSON 数据的索引,因为这种方法对于存在索引时和不存在索引时返回的结果不同(Bug#27337092):

mysql> CREATE TABLE employees (
         data JSON,
         generated_col VARCHAR(30) AS (CAST(data->>'$.name' AS CHAR(30)))
       );
Query OK, 0 rows affected, 1 warning (0.03 sec)
 
mysql> INSERT INTO employees (data)
       VALUES ('{"name": "james"}'), ('{"name": "James"}');
Query OK, 2 rows affected, 1 warning (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 1
 
mysql> SELECT * FROM employees WHERE data->>'$.name' = 'James';
+-------------------+---------------+
| data              | generated_col |
+-------------------+---------------+
| {"name": "James"} | James         |
+-------------------+---------------+
1 row in set (0.00 sec)
 
mysql> ALTER TABLE employees ADD INDEX idx (generated_col);
Query OK, 0 rows affected, 1 warning (0.03 sec)
Records: 0  Duplicates: 0  Warnings: 1
 
mysql> SELECT * FROM employees WHERE data->>'$.name' = 'James';
+-------------------+---------------+
| data              | generated_col |
+-------------------+---------------+
| {"name": "james"} | james         |
| {"name": "James"} | James         |
+-------------------+---------------+
2 rows in set (0.01 sec)

总结

(1)主键不支持函数索引,因为主键以实际列进行存储,而函数索引是作为虚拟列存在的

(2)在有主键的情况下,唯一索引支持函数索引;但在无主键的情况下,被提升为主键的唯一索引不支持

(3)外键不支持函数索引

(4)空间索引和全文索引不支持函数索引

(5)函数索引不能直接使用列前缀,可以通过SUBSTRING()和CAST()来替代

(6)在删除列之前,要先删除相关的函数索引

在MySQL 8.0中,引入了不可见索引、降序索引、函数索引的新特性,索引方面功能也是趋于完善。所以,大家还是尽快升级到8.0吧。

函数索引示例

准备数据

mysql> use testdb;
Database changed
mysql> create table   tb_function(id int primary key auto_increment,name  varchar(100),create_time datetime);
Query OK, 0 rows affected (0.01 sec)

mysql> insert into  tb_function(name,creatE_time) values('anniuadaOAIFAPUHIA','2020-07-01 12:00:00');
Query OK, 1 row affected (0.02 sec)

mysql> insert into  tb_function(name,creatE_time) values('CWQSsar3qcssg','2020-07-01 15:00:00');
Query OK, 1 row affected (0.01 sec)

mysql> insert into  tb_function(name,creatE_time) values('vxfqrt2adafz','2020-07-01 21:30:00');
Query OK, 1 row affected (0.01 sec)

mysql> insert into  tb_function(name,creatE_time) values('etxzwrwbdhegqgaheqhag','2020-07-02 01:30:00');
Query OK, 1 row affected (0.01 sec)

mysql> insert into  tb_function(name,creatE_time) values('awrs433fsgvsfwtwg','2020-07-02 03:30:00');
Query OK, 1 row affected (0.00 sec)

mysql> insert into  tb_function(name,creatE_time) values('awrs433fsgvsfwtwg','2020-07-02 07:32:00');
Query OK, 1 row affected (0.00 sec)

mysql> insert into  tb_function(name,creatE_time) values('awrs433fsgvsfwtwg','2020-07-02 10:32:00');
Query OK, 1 row affected (0.00 sec)

mysql> insert into  tb_function(name,creatE_time) values('tuilklmdadq','2020-07-02 15:32:00');
Query OK, 1 row affected (0.00 sec)

mysql> insert into  tb_function(name,creatE_time) values('wesv2wqdshehq','2020-07-02 20:32:00');
Query OK, 1 row affected (0.00 sec)

mysql> insert into  tb_function(name,creatE_time) values('89yoijnlkwr1','2020-07-03 02:56:00');
Query OK, 1 row affected (0.00 sec)

mysql> insert into  tb_function(name,creatE_time) values('olj;nsaaq','2020-07-03 08:41:00');
Query OK, 1 row affected (0.01 sec)

mysql> insert into  tb_function(name,creatE_time) values('ygo;jkdsaq','2020-07-03 16:20:00');
Query OK, 1 row affected (0.01 sec)

mysql> select  * from tb_function;
+----+-----------------------+---------------------+
| id | name                  | create_time         |
+----+-----------------------+---------------------+
|  1 | anniuadaOAIFAPUHIA    | 2020-07-01 12:00:00 |
|  2 | CWQSsar3qcssg         | 2020-07-01 15:00:00 |
|  3 | vxfqrt2adafz          | 2020-07-01 21:30:00 |
|  4 | etxzwrwbdhegqgaheqhag | 2020-07-02 01:30:00 |
|  5 | awrs433fsgvsfwtwg     | 2020-07-02 03:30:00 |
|  6 | awrs433fsgvsfwtwg     | 2020-07-02 07:32:00 |
|  7 | awrs433fsgvsfwtwg     | 2020-07-02 10:32:00 |
|  8 | tuilklmdadq           | 2020-07-02 15:32:00 |
|  9 | wesv2wqdshehq         | 2020-07-02 20:32:00 |
| 10 | 89yoijnlkwr1          | 2020-07-03 02:56:00 |
| 11 | olj;nsaaq             | 2020-07-03 08:41:00 |
| 12 | ygo;jkdsaq            | 2020-07-03 16:20:00 |
+----+-----------------------+---------------------+
12 rows in set (0.00 sec)

普通索引

普通索引: 执行计划中可以看出是进行了全面扫描

mysql> alter  table tb_function add key idx_create_time(create_time);
Query OK, 0 rows affected (0.13 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> select  *  from  tb_function  where   date(create_time)='2020-07-01';
+----+--------------------+---------------------+
| id | name               | create_time         |
+----+--------------------+---------------------+
|  1 | anniuadaOAIFAPUHIA | 2020-07-01 12:00:00 |
|  2 | CWQSsar3qcssg      | 2020-07-01 15:00:00 |
|  3 | vxfqrt2adafz       | 2020-07-01 21:30:00 |
+----+--------------------+---------------------+
3 rows in set (0.00 sec)

mysql> explain select  *  from  tb_function  where   date(create_time)='2020-07-01';
+----+-------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table       | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | tb_function | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   12 |   100.00 | Using where |
+----+-------------+-------------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

普通索引: 修改SQL写法来实现走索引(或者使用虚拟列的方式)

mysql> select  *  from  tb_function  where   create_time>='2020-07-01' and create_time<date_add('2020-07-01',INTERVAL 1 day);
+----+--------------------+---------------------+
| id | name               | create_time         |
+----+--------------------+---------------------+
|  1 | anniuadaOAIFAPUHIA | 2020-07-01 12:00:00 |
|  2 | CWQSsar3qcssg      | 2020-07-01 15:00:00 |
|  3 | vxfqrt2adafz       | 2020-07-01 21:30:00 |
+----+--------------------+---------------------+
3 rows in set (0.00 sec)
mysql> explain select  *  from  tb_function  where   create_time>='2020-07-01' and create_time<date_add('2020-07-01',INTERVAL 1 day);
+----+-------------+-------------+------------+-------+-----------------+-----------------+---------+------+------+----------+-----------------------+
| id | select_type | table       | partitions | type  | possible_keys   | key             | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-------------+------------+-------+-----------------+-----------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | tb_function | NULL       | range | idx_create_time | idx_create_time | 6       | NULL |    3 |   100.00 | Using index condition |
+----+-------------+-------------+------------+-------+-----------------+-----------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

函数索引

MySQL8.0的索引特性增加了函数索引。其实MySQL5.7中推出了虚拟列的功能,而MySQL8.0的函数索引也是依据虚拟列来实现的。

在将上述的表及数据在MySQL8.0的实例上创建,然后创建create_time的函数索引,SQL如下

mysql> alter  table tb_function add key idx_create_time((date(create_time))); --   注意里面字段的括号
Query OK, 0 rows affected (0.10 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> select  *  from  tb_function  where   date(create_time)='2020-07-01';
+----+--------------------+---------------------+
| id | name               | create_time         |
+----+--------------------+---------------------+
|  1 | anniuadaOAIFAPUHIA | 2020-07-01 12:00:00 |
|  2 | CWQSsar3qcssg      | 2020-07-01 15:00:00 |
|  3 | vxfqrt2adafz       | 2020-07-01 21:30:00 |
+----+--------------------+---------------------+
3 rows in set (0.00 sec)
mysql> explain select  *  from  tb_function  where   date(create_time)='2020-07-01';
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
| id | select_type | table       | partitions | type | possible_keys   | key             | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | tb_function | NULL       | ref  | idx_create_time | idx_create_time | 4       | const |    3 |   100.00 | NULL  |
+----+-------------+-------------+------------+------+-----------------+-----------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

可见,在MySQL8.0 创建对应的函数索引后,不改变SQL写法的前提下,查询的列上进行对应的函数计算后也可以走索引。

函数索引源码pach

WL#1075 Add support for functional indexes · mysql/mysql-server@e6f5f91 · GitHub

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐