MySQL8.0 函数索引

2023-11-09

MySQL8.0 函数索引

MySQL8.0.13 开始支持 在索引中使用函数的值

支持降序索引

支持JSON 数据的索引

函数索引基于虚拟列功能实现

创建表与索引

在 t3 表 上建立索引,创建普通索引和函数的索引

CREATE TABLE `t3` (
  `c1` varchar(10),
  `c2` varchar(10)
);


mysql> create index idx_c1 on t3(c1);
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0


-- 创建函数索引
mysql> create index idx_func_c2 on t3( (UPPER(c2)) );
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0
mysql> explain select * from t3 where upper(c1) = 'ABC' \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t3
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)




mysql> explain select * from t3 where upper(c2) = 'ABC' \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t3
   partitions: NULL
         type: ref
possible_keys: idx_func_c2
          key: idx_func_c2
      key_len: 43
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.01 sec)

可以看到 c2 这个字段 会走 函数的索引

针对JSON数据 创建索引

json 数据创建索引

CAST(x AS type) 可以将一个数据类型 转化为另一个数据类型。 type 取值有 BINARY, CHAR(n), DATE,TIME, DATETIME,DEMICAL,SIGNED,UNSIGNED 等

mysql> select cast(1100 as char(2));
+-----------------------+
| cast(1100 as char(2)) |
+-----------------------+
| 11                    |
+-----------------------+
1 row in set, 1 warning (0.00 sec)

将 1100 数字 转为字符串类型,结果为 '11'

create table t4(data json, index((CAST(data ->> '$.name' as char(30) ) )));


mysql> show index from t4\G
*************************** 1. row ***************************
        Table: t4
   Non_unique: 1
     Key_name: functional_index
 Seq_in_index: 1
  Column_name: NULL
    Collation: A
  Cardinality: 0
     Sub_part: NULL
       Packed: NULL
         Null: YES
   Index_type: BTREE
      Comment:
Index_comment:
      Visible: YES
   Expression: cast(json_unquote(json_extract(`data`,_latin1\'$.name\')) as char(30) charset latin1)
1 row in set (0.00 sec)
                                              

使用 explain 分析 是否可以使用索引.


mysql> explain select * from t4 where CAST(data ->> '$.name' as char(30)) = 'abc' \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t4
   partitions: NULL
         type: ref
possible_keys: functional_index
          key: functional_index
      key_len: 33
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

发现可以使用到索引

请添加图片描述

创建一个虚拟列

来给 t3 表 增加一列 ,增加一个虚拟计算列

mysql> alter table t3 add column c3 varchar(10) generated always as (upper(c1));

mysql> select * from t3;
Empty set (0.00 sec)

mysql> insert into t3(c1,c2) values('aa','frank');
Query OK, 1 row affected (0.01 sec)


mysql> select * from t3;
+------+-------+------+
| c1   | c2    | c3   |
+------+-------+------+
| aa   | frank | AA   |
+------+-------+------+
1 row in set (0.00 sec)

创建一个普通的索引 idx_c3

create index idx_c3 on t3(c3);
mysql> explain select * from t3 where c3='aa';
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t3    | NULL       | ref  | idx_c3        | idx_c3 | 43      | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

此时可以发现就走索引了. 相当于是 建立了一个函数索引

explain select * from t3 where upper(c1)='AAA';

mysql> explain select * from t3 where upper(c1)='AAA';
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | t3    | NULL       | ref  | idx_c3        | idx_c3 | 43      | const |    1 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------+

小问题

有一个 员工表 , id, 奖金 , 薪水 三列, 表结构如下

create table employee(
	id int auto_increment primary key,
	salary int not null,
	bonus int not null
);

经常需要 按照 salary 加上 bonus 的和值 进行查询并且 排序显示,如何设计索引?

create index idx_func on employee((salary + bonus));

show index from employee \G

这样做 是可以使用索引的

mysql> explain select * from employee  where salary + bonus =100 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employee
   partitions: NULL
         type: ref
possible_keys: idx_func
          key: idx_func
      key_len: 8
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

官方文档例子

example1 指定索引表达式与JSON_UNQUOTE()相同的排序方式

Assign the indexed expression the same collation as 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';


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


请添加图片描述

The ->> operator is the same as JSON_UNQUOTE(JSON_EXTRACT(...)), and JSON_UNQUOTE() returns a string with collation utf8mb4_bin. The comparison is thus case-sensitive, and only one row matches:

example2 在查询中指定完整的表达式

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 }');
  
explain SELECT * FROM employees WHERE CAST(data->>'$.name' AS CHAR(30)) = 'James';
 

请添加图片描述

SELECT * FROM employees WHERE CAST(data->>'$.name' AS CHAR(30)) = 'James';

CAST() returns a string with collation utf8mb4_0900_ai_ci, so the comparison case-insensitive and two rows match:

cast() 返回的 字符排序规则 是 utf8mb4_0900_ai_ci ,这个是大小写不敏感的,因此返回了两条数据。

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

总结

函数索引的功能 是MySQL8.0 以后才有的功能 ,如果有了这个特性,可以在使用函数的时候 使用这个索引啦,并且还支持JSON 的字段索引,是不是很方便呢?

参考文档

create-index-functional-key-parts

MySQL 8.0 新特性之函数索引

分享快乐,留住感动. '2022-10-16 19:55:36' --frank
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MySQL8.0 函数索引 的相关文章