在之前的版本中mysql中是没有函数索引的,所以对于在列上添加函数运算的查询只能走全表扫,在57中虽然没有明确的函数索引创建方式,但是可以通过虚拟列的方式变相实现。下面的例子是在oow大会中学习到的。
mysql> show create table employees;
+-----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| employees | CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` enum('M','F') NOT NULL,
`hire_date` date NOT NULL,
PRIMARY KEY (`emp_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> desc employees;
+------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------+
| emp_no | int(11) | NO | PRI | NULL | |
| birth_date | date | NO | | NULL | |
| first_name | varchar(14) | NO | | NULL | |
| last_name | varchar(16) | NO | | NULL | |
| gender | enum('M','F') | NO | | NULL | |
| hire_date | date | NO | | NULL | |
+------------+---------------+------+-----+---------+-------+
6 rows in set (0.00 sec)
mysql> select count(*) from employees;
+----------+
| count(*) |
+----------+
| 300024 |
+----------+
1 row in set (0.18 sec)
mysql> select * from employees limit 1\G
*************************** 1. row ***************************
emp_no: 10001
birth_date: 1953-09-02
first_name: Georgi
last_name: Facello
gender: M
hire_date: 1986-06-26
1 row in set (0.00 sec)
mysql> ALTER TABLE employees ADD COLUMN past_date
-> date GENERATED ALWAYS AS
-> (adddate(hire_date,INTERVAL 18 YEAR)) VIRTUAL;
Query OK, 0 rows affected (0.19 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc employees;
+------------+---------------+------+-----+---------+-------------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+-------------------+
| emp_no | int(11) | NO | PRI | NULL | |
| birth_date | date | NO | | NULL | |
| first_name | varchar(14) | NO | | NULL | |
| last_name | varchar(16) | NO | | NULL | |
| gender | enum('M','F') | NO | | NULL | |
| hire_date | date | NO | | NULL | |
| past_date | date | YES | | NULL | VIRTUAL GENERATED |
+------------+---------------+------+-----+---------+-------------------+
7 rows in set (0.01 sec)
mysql> select * from employees limit 1\G
*************************** 1. row ***************************
emp_no: 10001
birth_date: 1953-09-02
first_name: Georgi
last_name: Facello
gender: M
hire_date: 1986-06-26
past_date: 2004-06-26
1 row in set (0.00 sec)
mysql> select * from employees limit 2\G
*************************** 1. row ***************************
emp_no: 10001
birth_date: 1953-09-02
first_name: Georgi
last_name: Facello
gender: M
hire_date: 1986-06-26
past_date: 2004-06-26
*************************** 2. row ***************************
emp_no: 10002
birth_date: 1964-06-02
first_name: Bezalel
last_name: Simmel
gender: F
hire_date: 1985-11-21
past_date: 2003-11-21
2 rows in set (0.00 sec)
mysql> CREATE INDEX past_date_idx on
-> employees(past_date);
Query OK, 0 rows affected (4.48 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> show create table employees;
+-----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| employees | CREATE TABLE `employees` (
`emp_no` int(11) NOT NULL,
`birth_date` date NOT NULL,
`first_name` varchar(14) NOT NULL,
`last_name` varchar(16) NOT NULL,
`gender` enum('M','F') NOT NULL,
`hire_date` date NOT NULL,
`past_date` date GENERATED ALWAYS AS ((`hire_date` + interval 18 year)) VIRTUAL,
PRIMARY KEY (`emp_no`),
KEY `past_date_idx` (`past_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> explain select * from employees where
-> adddate(hire_date,INTERVAL 18 YEAR)>=NOW();
+----+-------------+-----------+------------+-------+---------------+---------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------+------------+-------+---------------+---------------+---------+------+------+----------+-------------+
| 1 | SIMPLE | employees | NULL | range | past_date_idx | past_date_idx | 4 | NULL | 1 | 100.00 | Using where |
+----+-------------+-----------+------------+-------+---------------+---------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
看到通过这种方式,对于现有的代码可以不用修改就可以使用上索引。